Add ImageIndexIterator.

We can use image index iterators to iterate over all images in a
Texture. This allows us to do some operations in TextureD3D rather
than in the typed subclasses and save on some repeated code.

BUG=angle:729

Change-Id: I3ba47b2eebad2cfca313117fd501ff76d5107044
Reviewed-on: https://chromium-review.googlesource.com/219834
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Brandon Jones <bajones@chromium.org>
diff --git a/src/libGLESv2/ImageIndex.cpp b/src/libGLESv2/ImageIndex.cpp
index 3522b99..684c5bf 100644
--- a/src/libGLESv2/ImageIndex.cpp
+++ b/src/libGLESv2/ImageIndex.cpp
@@ -54,4 +54,90 @@
       layerIndex(layerIndexIn)
 {}
 
+ImageIndexIterator ImageIndexIterator::Make2D(GLint minMip, GLint maxMip)
+{
+    return ImageIndexIterator(GL_TEXTURE_2D, rx::Range<GLint>(minMip, maxMip),
+                              rx::Range<GLint>(ImageIndex::ENTIRE_LEVEL, ImageIndex::ENTIRE_LEVEL), NULL);
+}
+
+ImageIndexIterator ImageIndexIterator::MakeCube(GLint minMip, GLint maxMip)
+{
+    return ImageIndexIterator(GL_TEXTURE_CUBE_MAP, rx::Range<GLint>(minMip, maxMip), rx::Range<GLint>(0, 6), NULL);
+}
+
+ImageIndexIterator ImageIndexIterator::Make3D(GLint minMip, GLint maxMip,
+                                              GLint minLayer, GLint maxLayer)
+{
+    return ImageIndexIterator(GL_TEXTURE_3D, rx::Range<GLint>(minMip, maxMip), rx::Range<GLint>(minLayer, maxLayer), NULL);
+}
+
+ImageIndexIterator ImageIndexIterator::Make2DArray(GLint minMip, GLint maxMip,
+                                                   const GLsizei *layerCounts)
+{
+    return ImageIndexIterator(GL_TEXTURE_2D_ARRAY, rx::Range<GLint>(minMip, maxMip),
+                              rx::Range<GLint>(0, IMPLEMENTATION_MAX_2D_ARRAY_TEXTURE_LAYERS), layerCounts);
+}
+
+ImageIndexIterator::ImageIndexIterator(GLenum type, const rx::Range<GLint> &mipRange,
+                                       const rx::Range<GLint> &layerRange, const GLsizei *layerCounts)
+    : mType(type),
+      mMipRange(mipRange),
+      mLayerRange(layerRange),
+      mLayerCounts(layerCounts),
+      mCurrentMip(mipRange.start),
+      mCurrentLayer(layerRange.start)
+{}
+
+GLint ImageIndexIterator::maxLayer() const
+{
+    return (mLayerCounts ? static_cast<GLint>(mLayerCounts[mCurrentMip]) : mLayerRange.end);
+}
+
+ImageIndex ImageIndexIterator::next()
+{
+    ASSERT(hasNext());
+
+    ImageIndex value = current();
+
+    // Iterate layers in the inner loop for now. We can add switchable
+    // layer or mip iteration if we need it.
+
+    if (mCurrentLayer != ImageIndex::ENTIRE_LEVEL)
+    {
+        if (mCurrentLayer < maxLayer()-1)
+        {
+            mCurrentLayer++;
+        }
+        else if (mCurrentMip < mMipRange.end-1)
+        {
+            mCurrentMip++;
+            mCurrentLayer = mLayerRange.start;
+        }
+    }
+    else if (mCurrentMip < mMipRange.end-1)
+    {
+        mCurrentMip++;
+        mCurrentLayer = mLayerRange.start;
+    }
+
+    return value;
+}
+
+ImageIndex ImageIndexIterator::current() const
+{
+    ImageIndex value(mType, mCurrentMip, mCurrentLayer);
+
+    if (mType == GL_TEXTURE_CUBE_MAP)
+    {
+        value.type = TextureCubeMap::layerIndexToTarget(mCurrentLayer);
+    }
+
+    return value;
+}
+
+bool ImageIndexIterator::hasNext() const
+{
+    return (mCurrentMip < mMipRange.end || mCurrentLayer < maxLayer());
+}
+
 }
diff --git a/src/libGLESv2/ImageIndex.h b/src/libGLESv2/ImageIndex.h
index 9f2df88..c16c36a 100644
--- a/src/libGLESv2/ImageIndex.h
+++ b/src/libGLESv2/ImageIndex.h
@@ -10,6 +10,7 @@
 #define LIBGLESV2_IMAGE_INDEX_H_
 
 #include "angle_gl.h"
+#include "common/mathutil.h"
 
 namespace gl
 {
@@ -20,6 +21,7 @@
     GLint mipIndex;
     GLint layerIndex;
 
+    ImageIndex(GLenum typeIn, GLint mipIndexIn, GLint layerIndexIn);
     ImageIndex(const ImageIndex &other);
     ImageIndex &operator=(const ImageIndex &other);
 
@@ -31,9 +33,33 @@
     static ImageIndex Make3D(GLint mipIndex, GLint layerIndex = ENTIRE_LEVEL);
 
     static const GLint ENTIRE_LEVEL = static_cast<GLint>(-1);
+};
+
+class ImageIndexIterator
+{
+  public:
+    static ImageIndexIterator Make2D(GLint minMip, GLint maxMip);
+    static ImageIndexIterator MakeCube(GLint minMip, GLint maxMip);
+    static ImageIndexIterator Make3D(GLint minMip, GLint maxMip, GLint minLayer, GLint maxLayer);
+    static ImageIndexIterator Make2DArray(GLint minMip, GLint maxMip, const GLsizei *layerCounts);
+
+    ImageIndex next();
+    ImageIndex current() const;
+    bool hasNext() const;
 
   private:
-    ImageIndex(GLenum typeIn, GLint mipIndexIn, GLint layerIndexIn);
+
+    ImageIndexIterator(GLenum type, const rx::Range<GLint> &mipRange,
+                       const rx::Range<GLint> &layerRange, const GLsizei *layerCounts);
+
+    GLint maxLayer() const;
+
+    GLenum mType;
+    rx::Range<GLint> mMipRange;
+    rx::Range<GLint> mLayerRange;
+    const GLsizei *mLayerCounts;
+    GLint mCurrentMip;
+    GLint mCurrentLayer;
 };
 
 }
diff --git a/src/libGLESv2/renderer/d3d/TextureD3D.cpp b/src/libGLESv2/renderer/d3d/TextureD3D.cpp
index 7bb6947..e522b85 100644
--- a/src/libGLESv2/renderer/d3d/TextureD3D.cpp
+++ b/src/libGLESv2/renderer/d3d/TextureD3D.cpp
@@ -739,6 +739,10 @@
     }
 }
 
+gl::ImageIndexIterator TextureD3D_2D::imageIterator() const
+{
+    return gl::ImageIndexIterator::Make2D(0, mTexStorage->getLevelCount());
+}
 
 TextureD3D_Cube::TextureD3D_Cube(Renderer *renderer)
     : TextureD3D(renderer),
@@ -1245,6 +1249,10 @@
     }
 }
 
+gl::ImageIndexIterator TextureD3D_Cube::imageIterator() const
+{
+    return gl::ImageIndexIterator::MakeCube(0, mTexStorage->getLevelCount());
+}
 
 TextureD3D_3D::TextureD3D_3D(Renderer *renderer)
     : TextureD3D(renderer),
@@ -1739,6 +1747,11 @@
     }
 }
 
+gl::ImageIndexIterator TextureD3D_3D::imageIterator() const
+{
+    return gl::ImageIndexIterator::Make3D(0, mTexStorage->getLevelCount(),
+                                          gl::ImageIndex::ENTIRE_LEVEL, gl::ImageIndex::ENTIRE_LEVEL);
+}
 
 TextureD3D_2DArray::TextureD3D_2DArray(Renderer *renderer)
     : TextureD3D(renderer),
@@ -2257,4 +2270,9 @@
     }
 }
 
+gl::ImageIndexIterator TextureD3D_2DArray::imageIterator() const
+{
+    return gl::ImageIndexIterator::Make2DArray(0, mTexStorage->getLevelCount(), mLayerCounts);
+}
+
 }
diff --git a/src/libGLESv2/renderer/d3d/TextureD3D.h b/src/libGLESv2/renderer/d3d/TextureD3D.h
index 41c7318..a60d57a 100644
--- a/src/libGLESv2/renderer/d3d/TextureD3D.h
+++ b/src/libGLESv2/renderer/d3d/TextureD3D.h
@@ -51,6 +51,9 @@
     virtual RenderTarget *getRenderTarget(const gl::ImageIndex &index) = 0;
     virtual unsigned int getRenderTargetSerial(const gl::ImageIndex &index) = 0;
 
+    // Returns an iterator over all "Images" for this particular Texture.
+    virtual gl::ImageIndexIterator imageIterator() const = 0;
+
   protected:
     void setImage(const gl::PixelUnpackState &unpack, GLenum type, const void *pixels, Image *image);
     bool subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
@@ -115,6 +118,8 @@
     virtual RenderTarget *getRenderTarget(const gl::ImageIndex &index);
     virtual unsigned int getRenderTargetSerial(const gl::ImageIndex &index);
 
+    virtual gl::ImageIndexIterator imageIterator() const;
+
   private:
     DISALLOW_COPY_AND_ASSIGN(TextureD3D_2D);
 
@@ -172,6 +177,8 @@
     virtual RenderTarget *getRenderTarget(const gl::ImageIndex &index);
     virtual unsigned int getRenderTargetSerial(const gl::ImageIndex &index);
 
+    virtual gl::ImageIndexIterator imageIterator() const;
+
   private:
     DISALLOW_COPY_AND_ASSIGN(TextureD3D_Cube);
 
@@ -229,6 +236,8 @@
     virtual RenderTarget *getRenderTarget(const gl::ImageIndex &index);
     virtual unsigned int getRenderTargetSerial(const gl::ImageIndex &index);
 
+    virtual gl::ImageIndexIterator imageIterator() const;
+
   private:
     DISALLOW_COPY_AND_ASSIGN(TextureD3D_3D);
 
@@ -285,6 +294,8 @@
     virtual RenderTarget *getRenderTarget(const gl::ImageIndex &index);
     virtual unsigned int getRenderTargetSerial(const gl::ImageIndex &index);
 
+    virtual gl::ImageIndexIterator imageIterator() const;
+
   private:
     DISALLOW_COPY_AND_ASSIGN(TextureD3D_2DArray);