Move incomplete texture logic to shared helper.

The incomplete texture handling is similar between the D3D and Vulkan
back-ends. We create 1x1 textures, initialize them to black, and bind
them when we detect incomplete textures. We would also bind incomplete
textures when we detect feedback loops. In the GL back-end, we
wouldn't detect feedback loops, and would allow the driver to handle
incompleteness.

Instead implement this in a shared helper class, and do the feedback
loop detection in the front-end for every back-end. This makes our
behaviour more consistent between back-ends, and prevents undefined
behaviour.

Because initializing multisample textures is tricky (they
can't be updated with TexImage calls) we do a bit of a workaround so
the back-end can clear the incomplete multisample texture initially.

This progresses the initial Vulkan textures implementation.

BUG=angleproject:2167

Change-Id: I79ddcc0711fcc986f2578a52ac6f701231d241ac
Reviewed-on: https://chromium-review.googlesource.com/700993
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 5970312..1d041b4 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -2274,25 +2274,25 @@
             ASSERT(static_cast<size_t>(textureUnitIndex) < mCompleteTextureCache.size());
             ASSERT(static_cast<size_t>(textureUnitIndex) < newActiveTextures.size());
 
-            if (texture != nullptr)
-            {
-                // Mark the texture binding bit as dirty if the texture completeness changes.
-                // TODO(jmadill): Use specific dirty bit for completeness change.
-                if (texture->isSamplerComplete(context, sampler))
-                {
-                    texture->syncState();
-                    mCompleteTextureCache[textureUnitIndex] = texture;
-                }
-                else
-                {
-                    mCompleteTextureCache[textureUnitIndex] = nullptr;
-                }
+            ASSERT(texture);
 
-                // Bind the texture unconditionally, to recieve completeness change notifications.
-                mCompleteTextureBindings[textureUnitIndex].bind(texture->getDirtyChannel());
-                newActiveTextures.set(textureUnitIndex);
-                mCompleteTexturesMask.set(textureUnitIndex);
+            // Mark the texture binding bit as dirty if the texture completeness changes.
+            // TODO(jmadill): Use specific dirty bit for completeness change.
+            if (texture->isSamplerComplete(context, sampler) &&
+                !mDrawFramebuffer->hasTextureAttachment(texture))
+            {
+                texture->syncState();
+                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)
             {