Cache the index ranges at the gl::Buffer and rx::IndexBuffer levels so that ranges do not need to be re-calculated for direct buffers.

Issue #451

Signed-off-by: Jamie Madill
Signed-off-by: Shannon Woods
Author: Geoff Lang
diff --git a/src/libGLESv2/renderer/IndexBuffer.cpp b/src/libGLESv2/renderer/IndexBuffer.cpp
index 3d5d7a7..e24583a 100644
--- a/src/libGLESv2/renderer/IndexBuffer.cpp
+++ b/src/libGLESv2/renderer/IndexBuffer.cpp
@@ -176,28 +176,9 @@
     }
 }
 
-unsigned int StaticIndexBufferInterface::lookupRange(intptr_t offset, GLsizei count, unsigned int *minIndex, unsigned int *maxIndex)
+IndexRangeCache *StaticIndexBufferInterface::getIndexRangeCache()
 {
-    IndexRange range = {offset, count};
-
-    std::map<IndexRange, IndexResult>::iterator res = mCache.find(range);
-
-    if (res == mCache.end())
-    {
-        return -1;
-    }
-
-    *minIndex = res->second.minIndex;
-    *maxIndex = res->second.maxIndex;
-    return res->second.streamOffset;
-}
-
-void StaticIndexBufferInterface::addRange(intptr_t offset, GLsizei count, unsigned int minIndex, unsigned int maxIndex, unsigned int streamOffset)
-{
-    IndexRange indexRange = {offset, count};
-    IndexResult indexResult = {minIndex, maxIndex, streamOffset};
-    mCache[indexRange] = indexResult;
+    return &mIndexRangeCache;
 }
 
 }
-
diff --git a/src/libGLESv2/renderer/IndexBuffer.h b/src/libGLESv2/renderer/IndexBuffer.h
index 1afbd62..98fa5fe 100644
--- a/src/libGLESv2/renderer/IndexBuffer.h
+++ b/src/libGLESv2/renderer/IndexBuffer.h
@@ -11,6 +11,7 @@
 #define LIBGLESV2_RENDERER_INDEXBUFFER_H_
 
 #include "common/angleutils.h"
+#include "libGLESv2/renderer/IndexRangeCache.h"
 
 namespace rx
 {
@@ -99,37 +100,10 @@
 
     virtual bool reserveBufferSpace(unsigned int size, GLenum indexType);
 
-    unsigned int lookupRange(intptr_t offset, GLsizei count, unsigned int *minIndex, unsigned int *maxIndex);   // Returns the offset into the index buffer, or -1 if not found
-    void addRange(intptr_t offset, GLsizei count, unsigned int minIndex, unsigned int maxIndex, unsigned int streamOffset);
+    IndexRangeCache *getIndexRangeCache();
 
   private:
-    struct IndexRange
-    {
-        intptr_t offset;
-        GLsizei count;
-
-        bool operator<(const IndexRange& rhs) const
-        {
-            if (offset != rhs.offset)
-            {
-                return offset < rhs.offset;
-            }
-            if (count != rhs.count)
-            {
-                return count < rhs.count;
-            }
-            return false;
-        }
-    };
-
-    struct IndexResult
-    {
-        unsigned int minIndex;
-        unsigned int maxIndex;
-        unsigned int streamOffset;
-    };
-
-    std::map<IndexRange, IndexResult> mCache;
+    IndexRangeCache mIndexRangeCache;
 };
 
 }
diff --git a/src/libGLESv2/renderer/IndexDataManager.cpp b/src/libGLESv2/renderer/IndexDataManager.cpp
index 723072b..5056bcb 100644
--- a/src/libGLESv2/renderer/IndexDataManager.cpp
+++ b/src/libGLESv2/renderer/IndexDataManager.cpp
@@ -13,6 +13,7 @@
 
 #include "libGLESv2/Buffer.h"
 #include "libGLESv2/main.h"
+#include "libGLESv2/formatutils.h"
 #include "libGLESv2/renderer/IndexBuffer.h"
 
 namespace rx
@@ -53,17 +54,6 @@
     delete mCountingBuffer;
 }
 
-static unsigned int indexTypeSize(GLenum type)
-{
-    switch (type)
-    {
-      case GL_UNSIGNED_INT:   return sizeof(GLuint);
-      case GL_UNSIGNED_SHORT: return sizeof(GLushort);
-      case GL_UNSIGNED_BYTE:  return sizeof(GLubyte);
-      default: UNREACHABLE(); return sizeof(GLushort);
-    }
-}
-
 static void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
 {
     if (type == GL_UNSIGNED_BYTE)
@@ -142,7 +132,7 @@
           default: UNREACHABLE(); alignedOffset = false;
         }
 
-        if (indexTypeSize(type) * count + offset > storage->getSize())
+        if (gl::GetTypeBytes(type) * count + offset > storage->getSize())
         {
             return GL_INVALID_OPERATION;
         }
@@ -162,18 +152,26 @@
     {
         indexBuffer = streamingBuffer;
         streamOffset = offset;
-        computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
+
+        if (!buffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex,
+                                                     &translated->maxIndex, NULL))
+        {
+            computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
+            buffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex,
+                                                   translated->maxIndex, offset);
+        }
     }
     else if (staticBuffer && staticBuffer->getBufferSize() != 0 && staticBuffer->getIndexType() == type && alignedOffset)
     {
         indexBuffer = staticBuffer;
-        streamOffset = staticBuffer->lookupRange(offset, count, &translated->minIndex, &translated->maxIndex);
 
-        if (streamOffset == -1)
+        if (!staticBuffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex,
+                                                           &translated->maxIndex, &streamOffset))
         {
-            streamOffset = (offset / indexTypeSize(type)) * indexTypeSize(destinationIndexType);
+            streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType);
             computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
-            staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
+            staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex,
+                                                         translated->maxIndex, streamOffset);
         }
     }
     else
@@ -185,7 +183,7 @@
             if (staticBuffer->getBufferSize() == 0 && alignedOffset)
             {
                 indexBuffer = staticBuffer;
-                convertCount = storage->getSize() / indexTypeSize(type);
+                convertCount = storage->getSize() / gl::GetTypeBytes(type);
             }
             else
             {
@@ -200,7 +198,7 @@
             return GL_INVALID_OPERATION;
         }
 
-        unsigned int bufferSizeRequired = convertCount * indexTypeSize(destinationIndexType);
+        unsigned int bufferSizeRequired = convertCount * gl::GetTypeBytes(destinationIndexType);
         indexBuffer->reserveBufferSpace(bufferSizeRequired, type);
 
         void* output = NULL;
@@ -223,20 +221,21 @@
 
         if (staticBuffer)
         {
-            streamOffset = (offset / indexTypeSize(type)) * indexTypeSize(destinationIndexType);
-            staticBuffer->addRange(offset, count, translated->minIndex, translated->maxIndex, streamOffset);
+            streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType);
+            staticBuffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex,
+                                                         translated->maxIndex, streamOffset);
         }
     }
 
     translated->storage = directStorage ? storage : NULL;
     translated->indexBuffer = indexBuffer->getIndexBuffer();
     translated->serial = directStorage ? storage->getSerial() : indexBuffer->getSerial();
-    translated->startIndex = streamOffset / indexTypeSize(destinationIndexType);
+    translated->startIndex = streamOffset / gl::GetTypeBytes(destinationIndexType);
     translated->startOffset = streamOffset;
 
     if (buffer)
     {
-        buffer->promoteStaticUsage(count * indexTypeSize(type));
+        buffer->promoteStaticUsage(count * gl::GetTypeBytes(type));
     }
 
     return GL_NO_ERROR;
diff --git a/src/libGLESv2/renderer/IndexRangeCache.cpp b/src/libGLESv2/renderer/IndexRangeCache.cpp
new file mode 100644
index 0000000..b504aa1
--- /dev/null
+++ b/src/libGLESv2/renderer/IndexRangeCache.cpp
@@ -0,0 +1,97 @@
+#include "precompiled.h"
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// IndexRangeCache.cpp: Defines the rx::IndexRangeCache class which stores information about
+// ranges of indices.
+
+#include "libGLESv2/renderer/IndexRangeCache.h"
+#include "libGLESv2/formatutils.h"
+#include "common/debug.h"
+#include <tuple>
+
+namespace rx
+{
+
+void IndexRangeCache::addRange(GLenum type, intptr_t offset, GLsizei count, unsigned int minIdx, unsigned int maxIdx, 
+                               unsigned int streamOffset)
+{
+    mIndexRangeCache[IndexRange(type, offset, count)] = IndexBounds(minIdx, maxIdx, streamOffset);
+}
+
+void IndexRangeCache::invalidateRange(unsigned int offset, unsigned int size)
+{
+    unsigned int invalidateStart = offset;
+    unsigned int invalidateEnd = offset + size;
+
+    IndexRangeMap::iterator i = mIndexRangeCache.begin();
+    while (i != mIndexRangeCache.end())
+    {
+        unsigned int rangeStart = i->second.streamOffset;
+        unsigned int rangeEnd = i->second.streamOffset + (gl::GetTypeBytes(i->first.type) * i->first.count);
+
+        if (invalidateEnd < rangeStart || invalidateStart > rangeEnd)
+        {
+            ++i;
+        }
+        else
+        {
+            i = mIndexRangeCache.erase(i);
+        }
+    }
+}
+
+bool IndexRangeCache::findRange(GLenum type, intptr_t offset, GLsizei count, unsigned int *outMinIndex,
+                                unsigned int *outMaxIndex, unsigned int *outStreamOffset) const
+{
+    IndexRangeMap::const_iterator i = mIndexRangeCache.find(IndexRange(type, offset, count));
+    if (i != mIndexRangeCache.end())
+    {
+        if (outMinIndex)     *outMinIndex = i->second.minIndex;
+        if (outMaxIndex)     *outMaxIndex = i->second.maxIndex;
+        if (outStreamOffset) *outStreamOffset = i->second.streamOffset;
+        return true;
+    }
+    else
+    {
+        if (outMinIndex)     *outMinIndex = 0;
+        if (outMaxIndex)     *outMaxIndex = 0;
+        if (outStreamOffset) *outStreamOffset = 0;
+        return false;
+    }
+}
+
+void IndexRangeCache::clear()
+{
+    mIndexRangeCache.clear();
+}
+
+IndexRangeCache::IndexRange::IndexRange()
+    : type(GL_NONE), offset(0), count(0)
+{
+}
+
+IndexRangeCache::IndexRange::IndexRange(GLenum typ, intptr_t off, GLsizei c)
+    : type(typ), offset(off), count(c)
+{
+}
+
+bool IndexRangeCache::IndexRange::operator<(const IndexRange& rhs) const
+{
+    return std::make_tuple(type, offset, count) < std::make_tuple(rhs.type, rhs.offset, rhs.count);
+}
+
+IndexRangeCache::IndexBounds::IndexBounds()
+    : minIndex(0), maxIndex(0), streamOffset(0)
+{
+}
+
+IndexRangeCache::IndexBounds::IndexBounds(unsigned int minIdx, unsigned int maxIdx, unsigned int offset)
+    : minIndex(minIdx), maxIndex(maxIdx), streamOffset(offset)
+{
+}
+
+}
diff --git a/src/libGLESv2/renderer/IndexRangeCache.h b/src/libGLESv2/renderer/IndexRangeCache.h
new file mode 100644
index 0000000..5a5ade1
--- /dev/null
+++ b/src/libGLESv2/renderer/IndexRangeCache.h
@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// IndexRangeCache.h: Defines the rx::IndexRangeCache class which stores information about
+// ranges of indices.
+
+#ifndef LIBGLESV2_RENDERER_INDEXRANGECACHE_H_
+#define LIBGLESV2_RENDERER_INDEXRANGECACHE_H_
+
+#include "common/angleutils.h"
+
+namespace rx
+{
+
+class IndexRangeCache
+{
+  public:
+    void addRange(GLenum type, intptr_t offset, GLsizei count, unsigned int minIdx, unsigned int maxIdx, 
+                  unsigned int streamOffset);
+    bool findRange(GLenum type, intptr_t offset, GLsizei count, unsigned int *outMinIndex,
+                   unsigned int *outMaxIndex, unsigned int *outStreamOffset) const;
+
+    void invalidateRange(unsigned int offset, unsigned int size);
+    void clear();
+
+  private:
+    struct IndexRange
+    {
+        GLenum type;
+        intptr_t offset;
+        GLsizei count;
+
+        IndexRange();
+        IndexRange(GLenum type, intptr_t offset, GLsizei count);
+
+        bool operator<(const IndexRange& rhs) const;
+    };
+
+    struct IndexBounds
+    {
+        unsigned int minIndex;
+        unsigned int maxIndex;
+        unsigned int streamOffset;
+
+        IndexBounds();
+        IndexBounds(unsigned int minIdx, unsigned int maxIdx, unsigned int offset);
+    };
+
+    typedef std::map<IndexRange, IndexBounds> IndexRangeMap;
+    IndexRangeMap mIndexRangeCache;
+};
+
+}
+
+#endif LIBGLESV2_RENDERER_INDEXRANGECACHE_H