Improvements to the gl::Range class.

Make this a proper class, fix the extends method (previously did not
work as expected), add a contains method, and add tests. Also add an
iterator helper class so we can iterate over the range with range-for
loops.

This also fixes the shader resource unsetting code, which was not
actually unsetting all the possible applied textures.

BUG=angleproject:2052

Change-Id: I2a6fa97f96ccb612ad01a5e3f24dc869c54c967b
Reviewed-on: https://chromium-review.googlesource.com/527318
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/common/mathutil.h b/src/common/mathutil.h
index 5b1dd99..ccfab21 100644
--- a/src/common/mathutil.h
+++ b/src/common/mathutil.h
@@ -557,38 +557,65 @@
 }
 
 template <typename T>
-struct Range
+class Range
 {
+  public:
     Range() {}
-    Range(T lo, T hi) : start(lo), end(hi) { ASSERT(lo <= hi); }
+    Range(T lo, T hi) : mLow(lo), mHigh(hi) {}
 
-    T start;
-    T end;
-
-    T length() const { return end - start; }
+    T length() const { return (empty() ? 0 : (mHigh - mLow)); }
 
     bool intersects(Range<T> other)
     {
-        if (start <= other.start)
+        if (mLow <= other.mLow)
         {
-            return other.start < end;
+            return other.mLow < mHigh;
         }
         else
         {
-            return start < other.end;
+            return mLow < other.mHigh;
         }
     }
 
+    // Assumes that end is non-inclusive.. for example, extending to 5 will make "end" 6.
     void extend(T value)
     {
-        start = value > start ? value : start;
-        end = value < end ? value : end;
+        mLow  = value < mLow ? value : mLow;
+        mHigh = value >= mHigh ? (value + 1) : mHigh;
     }
 
-    bool empty() const
+    bool empty() const { return mHigh <= mLow; }
+
+    bool contains(T value) const { return value >= mLow && value < mHigh; }
+
+    class Iterator final
     {
-        return end <= start;
-    }
+      public:
+        Iterator(T value) : mCurrent(value) {}
+
+        Iterator &operator++()
+        {
+            mCurrent++;
+            return *this;
+        }
+        bool operator==(const Iterator &other) const { return mCurrent == other.mCurrent; }
+        bool operator!=(const Iterator &other) const { return mCurrent != other.mCurrent; }
+        T operator*() const { return mCurrent; }
+
+      private:
+        T mCurrent;
+    };
+
+    Iterator begin() const { return Iterator(mLow); }
+
+    Iterator end() const { return Iterator(mHigh); }
+
+    T low() const { return mLow; }
+    T high() const { return mHigh; }
+
+  private:
+    T mLow;
+    T mHigh;
 };
 
 typedef Range<int> RangeI;
diff --git a/src/common/mathutil_unittest.cpp b/src/common/mathutil_unittest.cpp
index ad39b38..1949ad1 100644
--- a/src/common/mathutil_unittest.cpp
+++ b/src/common/mathutil_unittest.cpp
@@ -331,4 +331,38 @@
     EXPECT_EQ(0.0f, Ldexp(1.0f, -129));
 }
 
+// Test that Range::extend works as expected.
+TEST(MathUtilTest, RangeExtend)
+{
+    RangeI range(0, 0);
+
+    range.extend(5);
+    EXPECT_EQ(0, range.low());
+    EXPECT_EQ(6, range.high());
+    EXPECT_EQ(6, range.length());
+
+    range.extend(-1);
+    EXPECT_EQ(-1, range.low());
+    EXPECT_EQ(6, range.high());
+    EXPECT_EQ(7, range.length());
+
+    range.extend(10);
+    EXPECT_EQ(-1, range.low());
+    EXPECT_EQ(11, range.high());
+    EXPECT_EQ(12, range.length());
+}
+
+// Test that Range iteration works as expected.
+TEST(MathUtilTest, RangeIteration)
+{
+    RangeI range(0, 10);
+    int expected = 0;
+    for (int value : range)
+    {
+        EXPECT_EQ(expected, value);
+        expected++;
+    }
+    EXPECT_EQ(range.length(), expected);
+}
+
 }  // anonymous namespace
diff --git a/src/libANGLE/ImageIndex.cpp b/src/libANGLE/ImageIndex.cpp
index 2318541..657e3ad 100644
--- a/src/libANGLE/ImageIndex.cpp
+++ b/src/libANGLE/ImageIndex.cpp
@@ -139,14 +139,16 @@
                               nullptr);
 }
 
-ImageIndexIterator::ImageIndexIterator(GLenum type, const Range<GLint> &mipRange,
-                                       const Range<GLint> &layerRange, const GLsizei *layerCounts)
+ImageIndexIterator::ImageIndexIterator(GLenum type,
+                                       const Range<GLint> &mipRange,
+                                       const Range<GLint> &layerRange,
+                                       const GLsizei *layerCounts)
     : mType(type),
       mMipRange(mipRange),
       mLayerRange(layerRange),
       mLayerCounts(layerCounts),
-      mCurrentMip(mipRange.start),
-      mCurrentLayer(layerRange.start)
+      mCurrentMip(mipRange.low()),
+      mCurrentLayer(layerRange.low())
 {}
 
 GLint ImageIndexIterator::maxLayer() const
@@ -154,9 +156,9 @@
     if (mLayerCounts)
     {
         ASSERT(mCurrentMip >= 0);
-        return (mCurrentMip < mMipRange.end) ? mLayerCounts[mCurrentMip] : 0;
+        return (mCurrentMip < mMipRange.high()) ? mLayerCounts[mCurrentMip] : 0;
     }
-    return mLayerRange.end;
+    return mLayerRange.high();
 }
 
 ImageIndex ImageIndexIterator::next()
@@ -174,20 +176,20 @@
         {
             mCurrentLayer++;
         }
-        else if (mCurrentMip < mMipRange.end - 1)
+        else if (mCurrentMip < mMipRange.high() - 1)
         {
             mCurrentMip++;
-            mCurrentLayer = mLayerRange.start;
+            mCurrentLayer = mLayerRange.low();
         }
         else
         {
             done();
         }
     }
-    else if (mCurrentMip < mMipRange.end - 1)
+    else if (mCurrentMip < mMipRange.high() - 1)
     {
         mCurrentMip++;
-        mCurrentLayer = mLayerRange.start;
+        mCurrentLayer = mLayerRange.low();
     }
     else
     {
@@ -211,12 +213,12 @@
 
 bool ImageIndexIterator::hasNext() const
 {
-    return (mCurrentMip < mMipRange.end || mCurrentLayer < maxLayer());
+    return (mCurrentMip < mMipRange.high() || mCurrentLayer < maxLayer());
 }
 
 void ImageIndexIterator::done()
 {
-    mCurrentMip   = mMipRange.end;
+    mCurrentMip   = mMipRange.high();
     mCurrentLayer = maxLayer();
 }
 
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 2a45df2..6d18f2a 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -396,13 +396,13 @@
 
 bool ProgramState::isSamplerUniformIndex(GLuint index) const
 {
-    return index >= mSamplerUniformRange.start && index < mSamplerUniformRange.end;
+    return mSamplerUniformRange.contains(index);
 }
 
 GLuint ProgramState::getSamplerIndexFromUniformIndex(GLuint uniformIndex) const
 {
     ASSERT(isSamplerUniformIndex(uniformIndex));
-    return uniformIndex - mSamplerUniformRange.start;
+    return uniformIndex - mSamplerUniformRange.low();
 }
 
 Program::Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, GLuint handle)
@@ -949,8 +949,11 @@
                   "All bits of DrawBufferMask can be contained in an uint32_t");
     mState.mActiveOutputVariables = stream.readInt<uint32_t>();
 
-    stream.readInt(&mState.mSamplerUniformRange.start);
-    stream.readInt(&mState.mSamplerUniformRange.end);
+    unsigned int start = 0;
+    unsigned int end   = 0;
+    stream.readInt(&start);
+    stream.readInt(&end);
+    mState.mSamplerUniformRange = RangeUI(start, end);
 
     unsigned int samplerCount = stream.readInt<unsigned int>();
     for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
@@ -1091,8 +1094,8 @@
                   "All bits of DrawBufferMask can be contained in an uint32_t");
     stream.writeInt(static_cast<uint32_t>(mState.mActiveOutputVariables.to_ulong()));
 
-    stream.writeInt(mState.mSamplerUniformRange.start);
-    stream.writeInt(mState.mSamplerUniformRange.end);
+    stream.writeInt(mState.mSamplerUniformRange.low());
+    stream.writeInt(mState.mSamplerUniformRange.high());
 
     stream.writeInt(mState.mSamplerBindings.size());
     for (const auto &samplerBinding : mState.mSamplerBindings)
@@ -2048,17 +2051,19 @@
 
 void Program::linkSamplerBindings()
 {
-    mState.mSamplerUniformRange.end   = static_cast<unsigned int>(mState.mUniforms.size());
-    mState.mSamplerUniformRange.start = mState.mSamplerUniformRange.end;
-    auto samplerIter                  = mState.mUniforms.rbegin();
-    while (samplerIter != mState.mUniforms.rend() && samplerIter->isSampler())
+    unsigned int high = static_cast<unsigned int>(mState.mUniforms.size());
+    unsigned int low  = high;
+
+    for (auto samplerIter = mState.mUniforms.rbegin();
+         samplerIter != mState.mUniforms.rend() && samplerIter->isSampler(); ++samplerIter)
     {
-        --mState.mSamplerUniformRange.start;
-        ++samplerIter;
+        --low;
     }
+
+    mState.mSamplerUniformRange = RangeUI(low, high);
+
     // If uniform is a sampler type, insert it into the mSamplerBindings array.
-    for (unsigned int samplerIndex = mState.mSamplerUniformRange.start;
-         samplerIndex < mState.mUniforms.size(); ++samplerIndex)
+    for (unsigned int samplerIndex : mState.mSamplerUniformRange)
     {
         const auto &samplerUniform = mState.mUniforms[samplerIndex];
         GLenum textureType         = SamplerTypeToTextureType(samplerUniform.type);
@@ -2763,8 +2768,7 @@
 
 void Program::setUniformValuesFromBindingQualifiers()
 {
-    for (unsigned int samplerIndex = mState.mSamplerUniformRange.start;
-         samplerIndex < mState.mSamplerUniformRange.end; ++samplerIndex)
+    for (unsigned int samplerIndex : mState.mSamplerUniformRange)
     {
         const auto &samplerUniform = mState.mUniforms[samplerIndex];
         if (samplerUniform.binding != -1)
diff --git a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
index fd6173c..992454a 100644
--- a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -910,9 +910,7 @@
 
     auto &currentSRVs = (samplerType == gl::SAMPLER_VERTEX ? mCurVertexSRVs : mCurPixelSRVs);
 
-    gl::Range<size_t> clearRange(rangeStart, rangeStart);
-    clearRange.extend(std::min(rangeEnd, currentSRVs.highestUsed()));
-
+    gl::Range<size_t> clearRange(rangeStart, std::min(rangeEnd, currentSRVs.highestUsed()));
     if (clearRange.empty())
     {
         return gl::NoError();
@@ -921,18 +919,18 @@
     auto deviceContext = mRenderer->getDeviceContext();
     if (samplerType == gl::SAMPLER_VERTEX)
     {
-        deviceContext->VSSetShaderResources(static_cast<unsigned int>(rangeStart),
-                                            static_cast<unsigned int>(rangeEnd - rangeStart),
+        deviceContext->VSSetShaderResources(static_cast<unsigned int>(clearRange.low()),
+                                            static_cast<unsigned int>(clearRange.length()),
                                             &mNullSRVs[0]);
     }
     else
     {
-        deviceContext->PSSetShaderResources(static_cast<unsigned int>(rangeStart),
-                                            static_cast<unsigned int>(rangeEnd - rangeStart),
+        deviceContext->PSSetShaderResources(static_cast<unsigned int>(clearRange.low()),
+                                            static_cast<unsigned int>(clearRange.length()),
                                             &mNullSRVs[0]);
     }
 
-    for (size_t samplerIndex = rangeStart; samplerIndex < rangeEnd; ++samplerIndex)
+    for (size_t samplerIndex : clearRange)
     {
         currentSRVs.update(samplerIndex, nullptr);
     }