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/renderer/renderer_utils.cpp b/src/libANGLE/renderer/renderer_utils.cpp
index ed3f952..eb78ecd 100644
--- a/src/libANGLE/renderer/renderer_utils.cpp
+++ b/src/libANGLE/renderer/renderer_utils.cpp
@@ -13,7 +13,9 @@
 #include "image_util/imageformats.h"
 
 #include "libANGLE/AttributeMap.h"
+#include "libANGLE/Context.h"
 #include "libANGLE/formatutils.h"
+#include "libANGLE/renderer/ContextImpl.h"
 #include "libANGLE/renderer/Format.h"
 
 #include <string.h>
@@ -470,4 +472,86 @@
     }
 }
 
+// IncompleteTextureSet implementation.
+IncompleteTextureSet::IncompleteTextureSet()
+{
+}
+
+IncompleteTextureSet::~IncompleteTextureSet()
+{
+}
+
+void IncompleteTextureSet::onDestroy(const gl::Context *context)
+{
+    // Clear incomplete textures.
+    for (auto &incompleteTexture : mIncompleteTextures)
+    {
+        ANGLE_SWALLOW_ERR(incompleteTexture.second->onDestroy(context));
+        incompleteTexture.second.set(context, nullptr);
+    }
+    mIncompleteTextures.clear();
+}
+
+gl::Error IncompleteTextureSet::getIncompleteTexture(
+    const gl::Context *context,
+    GLenum type,
+    MultisampleTextureInitializer *multisampleInitializer,
+    gl::Texture **textureOut)
+{
+    auto iter = mIncompleteTextures.find(type);
+    if (iter != mIncompleteTextures.end())
+    {
+        *textureOut = iter->second.get();
+        return gl::NoError();
+    }
+
+    ContextImpl *implFactory = context->getImplementation();
+
+    const GLubyte color[] = {0, 0, 0, 255};
+    const gl::Extents colorSize(1, 1, 1);
+    const gl::PixelUnpackState unpack(1, 0);
+    const gl::Box area(0, 0, 0, 1, 1, 1);
+
+    // If a texture is external use a 2D texture for the incomplete texture
+    GLenum createType = (type == GL_TEXTURE_EXTERNAL_OES) ? GL_TEXTURE_2D : type;
+
+    gl::Texture *tex = new gl::Texture(implFactory, std::numeric_limits<GLuint>::max(), createType);
+    angle::UniqueObjectPointer<gl::Texture, gl::Context> t(tex, context);
+
+    if (createType == GL_TEXTURE_2D_MULTISAMPLE)
+    {
+        ANGLE_TRY(t->setStorageMultisample(context, createType, 1, GL_RGBA8, colorSize, true));
+    }
+    else
+    {
+        ANGLE_TRY(t->setStorage(context, createType, 1, GL_RGBA8, colorSize));
+    }
+
+    if (type == GL_TEXTURE_CUBE_MAP)
+    {
+        for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
+             face++)
+        {
+            ANGLE_TRY(
+                t->setSubImage(context, unpack, face, 0, area, GL_RGBA, GL_UNSIGNED_BYTE, color));
+        }
+    }
+    else if (type == GL_TEXTURE_2D_MULTISAMPLE)
+    {
+        // Call a specialized clear function to init a multisample texture.
+        ANGLE_TRY(multisampleInitializer->initializeMultisampleTextureToBlack(context, t.get()));
+    }
+    else
+    {
+        ANGLE_TRY(
+            t->setSubImage(context, unpack, createType, 0, area, GL_RGBA, GL_UNSIGNED_BYTE, color));
+    }
+
+    t->syncState();
+
+    mIncompleteTextures[type].set(context, t.release());
+    *textureOut = mIncompleteTextures[type].get();
+    return gl::NoError();
+}
+
 }  // namespace rx