Restrict use of TexStorage more in CopyImage.

We were being too permissive in some copyImage methods when checking
if we can create a render target or not. This would lead us to trying
to use an inconsistent TexStorage in some cases.

Note that we do need to create inconsistent TexStorage in cases where
we are doing a FBO copy *from* a mipmap-incomplete texture attachment.

BUG=angle:780

Change-Id: I5a849b5d8c53713e38cb5f5052a8bb88b3f56260
Reviewed-on: https://chromium-review.googlesource.com/224480
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Shannon Woods <shannonwoods@chromium.org>
diff --git a/src/libGLESv2/renderer/d3d/TextureD3D.cpp b/src/libGLESv2/renderer/d3d/TextureD3D.cpp
index 85deb28..9adfc37 100644
--- a/src/libGLESv2/renderer/d3d/TextureD3D.cpp
+++ b/src/libGLESv2/renderer/d3d/TextureD3D.cpp
@@ -431,6 +431,13 @@
     return gl::Error(GL_NO_ERROR);
 }
 
+bool TextureD3D::canCreateRenderTargetForImage(const gl::ImageIndex &index) const
+{
+    rx::Image *image = getImage(index);
+    bool levelsComplete = (isImageComplete(index) && isImageComplete(getImageIndex(0, 0)));
+    return (image->isRenderableFormat() && levelsComplete);
+}
+
 TextureD3D_2D::TextureD3D_2D(Renderer *renderer)
     : TextureD3D(renderer)
 {
@@ -640,8 +647,9 @@
     redefineImage(level, sizedInternalFormat, width, height);
 
     gl::Rectangle sourceRect(x, y, width, height);
+    gl::ImageIndex index = gl::ImageIndex::Make2D(level);
 
-    if (!mImageArray[level]->isRenderableFormat())
+    if (!canCreateRenderTargetForImage(index))
     {
         gl::Error error = mImageArray[level]->copy(0, 0, 0, sourceRect, source);
         if (error.isError())
@@ -681,11 +689,11 @@
 
     // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
     // the current level we're copying to is defined (with appropriate format, width & height)
-    bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
 
     gl::Rectangle sourceRect(x, y, width, height);
+    gl::ImageIndex index = gl::ImageIndex::Make2D(level);
 
-    if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
+    if (!canCreateRenderTargetForImage(index))
     {
         gl::Error error = mImageArray[level]->copy(xoffset, yoffset, 0, sourceRect, source);
         if (error.isError())
@@ -872,6 +880,11 @@
     return true;
 }
 
+bool TextureD3D_2D::isImageComplete(const gl::ImageIndex &index) const
+{
+    return isLevelComplete(index.mipIndex);
+}
+
 // Constructs a native texture resource from the texture images
 gl::Error TextureD3D_2D::initializeStorage(bool renderTarget)
 {
@@ -1177,8 +1190,9 @@
     redefineImage(faceIndex, level, sizedInternalFormat, width, height);
 
     gl::Rectangle sourceRect(x, y, width, height);
+    gl::ImageIndex index = gl::ImageIndex::MakeCube(target, level);
 
-    if (!mImageArray[faceIndex][level]->isRenderableFormat())
+    if (!canCreateRenderTargetForImage(index))
     {
         gl::Error error = mImageArray[faceIndex][level]->copy(0, 0, 0, sourceRect, source);
         if (error.isError())
@@ -1218,14 +1232,10 @@
 {
     int faceIndex = gl::TextureCubeMap::targetToLayerIndex(target);
 
-    // We can only make our texture storage to a render target if the level we're copying *to* is complete
-    // and the base level is cube-complete. The base level must be cube complete (common case) because we cannot
-    // rely on the "getBaseLevel*" methods reliably otherwise.
-    bool canCreateRenderTarget = isFaceLevelComplete(faceIndex, level) && isCubeComplete();
-
     gl::Rectangle sourceRect(x, y, width, height);
+    gl::ImageIndex index = gl::ImageIndex::MakeCube(target, level);
 
-    if (!mImageArray[faceIndex][level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
+    if (!canCreateRenderTargetForImage(index))
     {
         gl::Error error =mImageArray[faceIndex][level]->copy(0, 0, 0, sourceRect, source);
         if (error.isError())
@@ -1526,6 +1536,11 @@
     return true;
 }
 
+bool TextureD3D_Cube::isImageComplete(const gl::ImageIndex &index) const
+{
+    return isFaceLevelComplete(index.layerIndex, index.mipIndex);
+}
+
 gl::Error TextureD3D_Cube::updateStorageFaceLevel(int faceIndex, int level)
 {
     ASSERT(level >= 0 && faceIndex < 6 && level < (int)ArraySize(mImageArray[faceIndex]) && mImageArray[faceIndex][level] != NULL);
@@ -1826,13 +1841,10 @@
 {
     ASSERT(target == GL_TEXTURE_3D);
 
-    // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
-    // the current level we're copying to is defined (with appropriate format, width & height)
-    bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
-
     gl::Rectangle sourceRect(x, y, width, height);
+    gl::ImageIndex index = gl::ImageIndex::Make3D(level);
 
-    if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
+    if (canCreateRenderTargetForImage(index))
     {
         gl::Error error = mImageArray[level]->copy(xoffset, yoffset, zoffset, sourceRect, source);
         if (error.isError())
@@ -2106,6 +2118,11 @@
     return true;
 }
 
+bool TextureD3D_3D::isImageComplete(const gl::ImageIndex &index) const
+{
+    return isLevelComplete(index.mipIndex);
+}
+
 gl::Error TextureD3D_3D::updateStorageLevel(int level)
 {
     ASSERT(level >= 0 && level < (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
@@ -2368,13 +2385,10 @@
 {
     ASSERT(target == GL_TEXTURE_2D_ARRAY);
 
-    // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
-    // the current level we're copying to is defined (with appropriate format, width & height)
-    bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
-
     gl::Rectangle sourceRect(x, y, width, height);
+    gl::ImageIndex index = gl::ImageIndex::Make2DArray(level, zoffset);
 
-    if (!mImageArray[level][0]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
+    if (canCreateRenderTargetForImage(index))
     {
         gl::Error error = mImageArray[level][zoffset]->copy(xoffset, yoffset, 0, sourceRect, source);
         if (error.isError())
@@ -2645,6 +2659,11 @@
     return true;
 }
 
+bool TextureD3D_2DArray::isImageComplete(const gl::ImageIndex &index) const
+{
+    return isLevelComplete(index.mipIndex);
+}
+
 gl::Error TextureD3D_2DArray::updateStorageLevel(int level)
 {
     ASSERT(level >= 0 && level < (int)ArraySize(mLayerCounts));
diff --git a/src/libGLESv2/renderer/d3d/TextureD3D.h b/src/libGLESv2/renderer/d3d/TextureD3D.h
index 19dba98..c951db6 100644
--- a/src/libGLESv2/renderer/d3d/TextureD3D.h
+++ b/src/libGLESv2/renderer/d3d/TextureD3D.h
@@ -77,7 +77,9 @@
     int mipLevels() const;
     virtual void initMipmapsImages() = 0;
     bool isBaseImageZeroSize() const;
+    virtual bool isImageComplete(const gl::ImageIndex &index) const = 0;
 
+    bool canCreateRenderTargetForImage(const gl::ImageIndex &index) const;
     virtual gl::Error ensureRenderTarget();
 
     virtual gl::Error createCompleteStorage(bool renderTarget, TextureStorage **outTexStorage) const = 0;
@@ -149,6 +151,7 @@
 
     bool isValidLevel(int level) const;
     bool isLevelComplete(int level) const;
+    virtual bool isImageComplete(const gl::ImageIndex &index) const;
 
     gl::Error updateStorageLevel(int level);
 
@@ -205,6 +208,7 @@
     bool isValidFaceLevel(int faceIndex, int level) const;
     bool isFaceLevelComplete(int faceIndex, int level) const;
     bool isCubeComplete() const;
+    virtual bool isImageComplete(const gl::ImageIndex &index) const;
     gl::Error updateStorageFaceLevel(int faceIndex, int level);
 
     void redefineImage(int faceIndex, GLint level, GLenum internalformat, GLsizei width, GLsizei height);
@@ -258,6 +262,7 @@
 
     bool isValidLevel(int level) const;
     bool isLevelComplete(int level) const;
+    virtual bool isImageComplete(const gl::ImageIndex &index) const;
     gl::Error updateStorageLevel(int level);
 
     void redefineImage(GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
@@ -310,6 +315,7 @@
 
     bool isValidLevel(int level) const;
     bool isLevelComplete(int level) const;
+    virtual bool isImageComplete(const gl::ImageIndex &index) const;
     gl::Error updateStorageLevel(int level);
 
     void deleteImages();