Support translating indices.

TRAC #11393

Signed-off-by: Nicolas Capens
Signed-off-by: Daniel Koch
Author:    Andrew Lewycky

git-svn-id: https://angleproject.googlecode.com/svn/trunk@72 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index b330b0d..f13db41 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -23,6 +23,7 @@
 #include "utilities.h"
 #include "geometry/backend.h"
 #include "geometry/VertexDataManager.h"
+#include "geometry/IndexDataManager.h"
 #include "geometry/dx9.h"
 
 #undef near
@@ -138,6 +139,7 @@
 
     mBufferBackEnd = NULL;
     mVertexDataManager = NULL;
+    mIndexDataManager = NULL;
 
     mInvalidEnum = false;
     mInvalidValue = false;
@@ -166,6 +168,7 @@
 
     delete mBufferBackEnd;
     delete mVertexDataManager;
+    delete mIndexDataManager;
 
     while (!mBufferMap.empty())
     {
@@ -206,6 +209,7 @@
     {
         mBufferBackEnd = new Dx9BackEnd(device);
         mVertexDataManager = new VertexDataManager(this, mBufferBackEnd);
+        mIndexDataManager = new IndexDataManager(this, mBufferBackEnd);
     }
 
     // Wrap the existing Direct3D 9 resources into GL objects and assign them to the '0' names
@@ -1020,50 +1024,26 @@
 
     lookupAttributeMapping(translated);
 
-    mBufferBackEnd->preDraw(translated);
+    mBufferBackEnd->setupAttributesPreDraw(translated);
 }
 
-void Context::applyVertexBuffer(GLsizei count, const void *indices, GLenum indexType)
+void Context::applyVertexBuffer(const TranslatedIndexData &indexInfo)
 {
     TranslatedAttribute translated[MAX_VERTEX_ATTRIBS];
 
-    mVertexDataManager->preRenderValidate(adjustIndexPointer(indices), count, translated);
+    mVertexDataManager->preRenderValidate(indexInfo, translated);
 
     lookupAttributeMapping(translated);
 
-    mBufferBackEnd->preDraw(translated);
+    mBufferBackEnd->setupAttributesPreDraw(translated);
 }
 
 // Applies the indices and element array bindings to the Direct3D 9 device
-void Context::applyIndexBuffer(const void *indices, GLsizei count)
+TranslatedIndexData Context::applyIndexBuffer(const void *indices, GLsizei count, GLenum mode, GLenum type)
 {
-    GLsizei length = count * sizeof(Index);
-
-    IDirect3DDevice9 *device = getDevice();
-
-    IDirect3DIndexBuffer9 *indexBuffer = NULL;
-    void *data;
-
-    HRESULT result = device->CreateIndexBuffer(length, 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &indexBuffer, NULL);
-
-    if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY)
-    {
-        return error(GL_OUT_OF_MEMORY);
-    }
-
-    ASSERT(SUCCEEDED(result));
-
-    if (indexBuffer)
-    {
-        indexBuffer->Lock(0, length, &data, 0);
-        memcpy(data, adjustIndexPointer(indices), length);
-        indexBuffer->Unlock();
-
-        device->SetIndices(indexBuffer);
-        indexBuffer->Release();   // Will only effectively be deleted when no longer in use
-    }
-
-    startIndex = 0;
+    TranslatedIndexData indexInfo = mIndexDataManager->preRenderValidate(mode, type, count, getBuffer(elementArrayBuffer), indices);
+    mBufferBackEnd->setupIndicesPreDraw(indexInfo);
+    return indexInfo;
 }
 
 // Applies the shaders and shader constants to the Direct3D 9 device
@@ -1480,7 +1460,7 @@
     if (!cullSkipsDraw(mode))
     {
         device->BeginScene();
-        device->DrawPrimitive(primitiveType, first, primitiveCount);
+        device->DrawPrimitive(primitiveType, 0, primitiveCount);
         device->EndScene();
     }
 }
@@ -1515,15 +1495,15 @@
     }
 
     applyState();
-    applyVertexBuffer(count, indices, type);
-    applyIndexBuffer(indices, count);
+    TranslatedIndexData indexInfo = applyIndexBuffer(indices, count, mode, type);
+    applyVertexBuffer(indexInfo);
     applyShaders();
     applyTextures();
 
     if (!cullSkipsDraw(mode))
     {
         device->BeginScene();
-        device->DrawIndexedPrimitive(primitiveType, 0, 0, count, startIndex, primitiveCount);
+        device->DrawIndexedPrimitive(primitiveType, -(INT)indexInfo.minIndex, indexInfo.minIndex, indexInfo.maxIndex-indexInfo.minIndex+1, indexInfo.offset/sizeof(Index), primitiveCount);
         device->EndScene();
     }
 }
diff --git a/src/libGLESv2/Context.h b/src/libGLESv2/Context.h
index 65cf8ec..649114a 100644
--- a/src/libGLESv2/Context.h
+++ b/src/libGLESv2/Context.h
@@ -30,6 +30,7 @@
 namespace gl
 {
 struct TranslatedAttribute;
+struct TranslatedIndexData;
 
 class Buffer;
 class Shader;
@@ -43,6 +44,7 @@
 class Depthbuffer;
 class Stencilbuffer;
 class VertexDataManager;
+class IndexDataManager;
 class BufferBackEnd;
 
 enum
@@ -189,8 +191,6 @@
     AttributeState vertexAttribute[MAX_VERTEX_ATTRIBS];
     GLuint samplerTexture[SAMPLER_TYPE_COUNT][MAX_TEXTURE_IMAGE_UNITS];
 
-    unsigned int startIndex;
-
     GLint unpackAlignment;
     GLint packAlignment;
 };
@@ -257,8 +257,8 @@
     bool applyRenderTarget(bool ignoreViewport);
     void applyState();
     void applyVertexBuffer(GLint first, GLsizei count);
-    void applyVertexBuffer(GLsizei count, const void *indices, GLenum indexType);
-    void applyIndexBuffer(const void *indices, GLsizei count);
+    void applyVertexBuffer(const TranslatedIndexData &indexInfo);
+    TranslatedIndexData applyIndexBuffer(const void *indices, GLsizei count, GLenum mode, GLenum type);
     void applyShaders();
     void applyTextures();
 
@@ -321,6 +321,7 @@
 
     BufferBackEnd *mBufferBackEnd;
     VertexDataManager *mVertexDataManager;
+    IndexDataManager *mIndexDataManager;
 
     Texture *mIncompleteTextures[SAMPLER_TYPE_COUNT];
 
diff --git a/src/libGLESv2/geometry/IndexDataManager.cpp b/src/libGLESv2/geometry/IndexDataManager.cpp
new file mode 100644
index 0000000..b0e864c
--- /dev/null
+++ b/src/libGLESv2/geometry/IndexDataManager.cpp
@@ -0,0 +1,119 @@
+//
+// Copyright (c) 2002-2010 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.
+//
+
+// geometry/IndexDataManager.cpp: Defines the IndexDataManager, a class that
+// runs the Buffer translation process for index buffers.
+
+#include "geometry/IndexDataManager.h"
+
+#include "common/debug.h"
+#include "Buffer.h"
+#include "geometry/backend.h"
+
+namespace
+{
+    enum { INITIAL_INDEX_BUFFER_SIZE = sizeof(gl::Index) * 8192 };
+}
+
+namespace gl
+{
+
+IndexDataManager::IndexDataManager(Context *context, BufferBackEnd *backend)
+    : mContext(context), mBackend(backend)
+{
+    mStreamBuffer = mBackend->createIndexBuffer(INITIAL_INDEX_BUFFER_SIZE);
+}
+
+IndexDataManager::~IndexDataManager()
+{
+    delete mStreamBuffer;
+}
+
+namespace
+{
+
+template <class InputIndexType>
+void copyIndices(const InputIndexType *in, GLsizei count, Index *out, GLuint *minIndex, GLuint *maxIndex)
+{
+    GLuint minIndexSoFar = *in;
+    GLuint maxIndexSoFar = *in;
+
+    for (GLsizei i = 0; i < count; i++)
+    {
+        if (minIndexSoFar > *in) minIndexSoFar = *in;
+        if (maxIndexSoFar < *in) maxIndexSoFar = *in;
+
+        *out++ = *in++;
+    }
+
+    *minIndex = minIndexSoFar;
+    *maxIndex = maxIndexSoFar;
+}
+
+}
+
+TranslatedIndexData IndexDataManager::preRenderValidate(GLenum mode, GLenum type, GLsizei count, Buffer *arrayElementBuffer, const void *indices)
+{
+    ASSERT(type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_BYTE);
+    ASSERT(count > 0);
+
+    TranslatedIndexData translated;
+
+    translated.count = count;
+
+    std::size_t requiredSpace = spaceRequired(mode, type, count);
+
+    if (requiredSpace > mStreamBuffer->size())
+    {
+        std::size_t newSize = std::max(requiredSpace, 2 * mStreamBuffer->size());
+
+        TranslatedIndexBuffer *newStreamBuffer = mBackend->createIndexBuffer(newSize);
+
+        delete mStreamBuffer;
+        mStreamBuffer = newStreamBuffer;
+    }
+
+    mStreamBuffer->reserveSpace(requiredSpace);
+
+    size_t offset;
+    void *output = mStreamBuffer->map(requiredSpace, &offset);
+
+    translated.buffer = mStreamBuffer;
+    translated.offset = offset;
+
+    translated.indices = static_cast<const Index*>(output);
+
+    if (arrayElementBuffer != NULL)
+    {
+        indices = static_cast<const GLubyte*>(arrayElementBuffer->data()) + reinterpret_cast<GLsizei>(indices);
+    }
+
+    Index *out = static_cast<Index*>(output);
+
+    if (type == GL_UNSIGNED_SHORT)
+    {
+        const GLushort *in = static_cast<const GLushort*>(indices);
+
+        copyIndices(in, count, out, &translated.minIndex, &translated.maxIndex);
+    }
+    else
+    {
+        const GLubyte *in = static_cast<const GLubyte*>(indices);
+
+        copyIndices(in, count, out, &translated.minIndex, &translated.maxIndex);
+    }
+
+    mStreamBuffer->unmap();
+
+    return translated;
+}
+
+std::size_t IndexDataManager::spaceRequired(GLenum mode, GLenum type, GLsizei count)
+{
+    return count * sizeof(Index);
+}
+
+}
diff --git a/src/libGLESv2/geometry/IndexDataManager.h b/src/libGLESv2/geometry/IndexDataManager.h
new file mode 100644
index 0000000..d590a9b
--- /dev/null
+++ b/src/libGLESv2/geometry/IndexDataManager.h
@@ -0,0 +1,60 @@
+//
+// Copyright (c) 2002-2010 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.
+//
+
+// geometry/IndexDataManager.h: Defines the IndexDataManager, a class that
+// runs the Buffer translation process for index buffers.
+
+#ifndef LIBGLESV2_GEOMETRY_INDEXDATAMANAGER_H_
+#define LIBGLESV2_GEOMETRY_INDEXDATAMANAGER_H_
+
+#include <bitset>
+#include <cstddef>
+
+#define GL_APICALL
+#include <GLES2/gl2.h>
+
+#include "Context.h"
+
+namespace gl
+{
+
+class Buffer;
+class BufferBackEnd;
+class TranslatedIndexBuffer;
+struct FormatConverter;
+
+struct TranslatedIndexData
+{
+    GLuint minIndex;
+    GLuint maxIndex;
+    GLuint count;
+
+    const Index *indices;
+
+    TranslatedIndexBuffer *buffer;
+    GLsizei offset;
+};
+
+class IndexDataManager
+{
+  public:
+    IndexDataManager(Context *context, BufferBackEnd *backend);
+    ~IndexDataManager();
+
+    TranslatedIndexData preRenderValidate(GLenum mode, GLenum type, GLsizei count, Buffer *arrayElementBuffer, const void *indices);
+
+  private:
+    std::size_t spaceRequired(GLenum mode, GLenum type, GLsizei count);
+
+    Context *mContext;
+    BufferBackEnd *mBackend;
+
+    TranslatedIndexBuffer *mStreamBuffer;
+};
+
+}
+
+#endif   // LIBGLESV2_GEOMETRY_INDEXDATAMANAGER_H_
diff --git a/src/libGLESv2/geometry/VertexDataManager.cpp b/src/libGLESv2/geometry/VertexDataManager.cpp
index 120d928..c11feba 100644
--- a/src/libGLESv2/geometry/VertexDataManager.cpp
+++ b/src/libGLESv2/geometry/VertexDataManager.cpp
@@ -14,6 +14,7 @@
 
 #include "Buffer.h"
 #include "geometry/backend.h"
+#include "geometry/IndexDataManager.h"
 
 namespace
 {
@@ -51,17 +52,17 @@
 
 void VertexDataManager::ArrayTranslationHelper::translate(const FormatConverter &converter, GLsizei stride, const void *source, void *dest)
 {
-    converter.convertArray(source, stride, mFirst+mCount, dest);
+    converter.convertArray(source, stride, mCount, dest);
 }
 
-VertexDataManager::IndexedTranslationHelper::IndexedTranslationHelper(const Index *indices, GLsizei count)
-    : mIndices(indices), mCount(count)
+VertexDataManager::IndexedTranslationHelper::IndexedTranslationHelper(const Index *indices, Index minIndex, GLsizei count)
+    : mIndices(indices), mMinIndex(minIndex), mCount(count)
 {
 }
 
 void VertexDataManager::IndexedTranslationHelper::translate(const FormatConverter &converter, GLsizei stride, const void *source, void *dest)
 {
-    converter.convertIndexed(source, stride, mCount, mIndices, dest);
+    converter.convertIndexed(source, stride, mMinIndex, mCount, mIndices, dest);
 }
 
 std::bitset<MAX_VERTEX_ATTRIBS> VertexDataManager::activeAttribs()
@@ -85,43 +86,16 @@
 {
     ArrayTranslationHelper translationHelper(start, count);
 
-    return internalPreRenderValidate(mContext->vertexAttribute, activeAttribs(), start, start+count, &translationHelper, outAttribs);
+    return internalPreRenderValidate(mContext->vertexAttribute, activeAttribs(), start, start+count-1, &translationHelper, outAttribs);
 }
 
-namespace
-{
-
-void indexRange(const Index *indices, std::size_t count, Index *minOut, Index *maxOut)
-{
-    ASSERT(count > 0);
-
-    Index minSoFar = indices[0];
-    Index maxSoFar = indices[0];
-
-    for (std::size_t i = 1; i < count; i++)
-    {
-        if (indices[i] > maxSoFar) maxSoFar = indices[i];
-        if (indices[i] < minSoFar) minSoFar = indices[i];
-    }
-
-    *minOut = minSoFar;
-    *maxOut = maxSoFar;
-}
-
-}
-
-GLenum VertexDataManager::preRenderValidate(const Index *indices, GLsizei count,
+GLenum VertexDataManager::preRenderValidate(const TranslatedIndexData &indexInfo,
                                             TranslatedAttribute *outAttribs)
 
 {
-    Index minIndex;
-    Index maxIndex;
+    IndexedTranslationHelper translationHelper(indexInfo.indices, indexInfo.minIndex, indexInfo.count);
 
-    indexRange(indices, count, &minIndex, &maxIndex);
-
-    IndexedTranslationHelper translationHelper(indices, count);
-
-    return internalPreRenderValidate(mContext->vertexAttribute, activeAttribs(), minIndex, maxIndex, &translationHelper, outAttribs);
+    return internalPreRenderValidate(mContext->vertexAttribute, activeAttribs(), indexInfo.minIndex, indexInfo.maxIndex, &translationHelper, outAttribs);
 }
 
 GLenum VertexDataManager::internalPreRenderValidate(const AttributeState *attribs,
@@ -159,8 +133,8 @@
                 translated[i].type = attribs[i].mType;
                 translated[i].size = attribs[i].mSize;
                 translated[i].normalized = attribs[i].mNormalized;
-                translated[i].offset = static_cast<std::size_t>(static_cast<const char*>(attribs[i].mPointer) - static_cast<const char*>(NULL));
                 translated[i].stride = interpretGlStride(attribs[i]);
+                translated[i].offset = static_cast<std::size_t>(static_cast<const char*>(attribs[i].mPointer) - static_cast<const char*>(NULL)) + translated[i].stride * minIndex;
                 translated[i].buffer = mContext->getBuffer(attribs[i].mBoundBuffer)->identityBuffer();
             }
             else
@@ -173,7 +147,7 @@
     // Handle any attributes needing translation or lifting.
     if (translateOrLift.any())
     {
-        std::size_t count = maxIndex + 1;
+        std::size_t count = maxIndex - minIndex + 1;
 
         std::size_t requiredSpace = 0;
 
@@ -222,6 +196,10 @@
                     input = attribs[i].mPointer;
                 }
 
+                size_t inputStride = interpretGlStride(attribs[i]);
+
+                input = static_cast<const char*>(input) + inputStride * minIndex;
+
                 translator->translate(formatConverter, interpretGlStride(attribs[i]), input, output);
 
                 mStreamBuffer->unmap();
diff --git a/src/libGLESv2/geometry/VertexDataManager.h b/src/libGLESv2/geometry/VertexDataManager.h
index 8f2fbd9..0d19261 100644
--- a/src/libGLESv2/geometry/VertexDataManager.h
+++ b/src/libGLESv2/geometry/VertexDataManager.h
@@ -26,6 +26,7 @@
 class TranslatedVertexBuffer;
 struct TranslatedAttribute;
 struct FormatConverter;
+struct TranslatedIndexData;
 
 class VertexDataManager
 {
@@ -39,8 +40,7 @@
                              GLsizei count,
                              TranslatedAttribute *outAttribs);
 
-    GLenum preRenderValidate(const Index *indices,
-                             GLsizei count,
+    GLenum preRenderValidate(const TranslatedIndexData &indexInfo,
                              TranslatedAttribute* outAttribs);
 
   private:
@@ -69,12 +69,13 @@
     class IndexedTranslationHelper : public TranslationHelper
     {
       public:
-        IndexedTranslationHelper(const Index *indices, GLsizei count);
+        IndexedTranslationHelper(const Index *indices, Index minIndex, GLsizei count);
 
         void translate(const FormatConverter &converter, GLint stride, const void *source, void *dest);
 
       private:
         const Index *mIndices;
+        Index mMinIndex;
         GLsizei mCount;
     };
 
diff --git a/src/libGLESv2/geometry/backend.h b/src/libGLESv2/geometry/backend.h
index 94ba37b..8c3cd04 100644
--- a/src/libGLESv2/geometry/backend.h
+++ b/src/libGLESv2/geometry/backend.h
@@ -26,7 +26,7 @@
 {
     bool identity;
     std::size_t outputVertexSize;
-    void (*convertIndexed)(const void *in, std::size_t stride, std::size_t n, const Index *indices, void *out);
+    void (*convertIndexed)(const void *in, std::size_t stride, Index minIndex, std::size_t n, const Index *indices, void *out);
     void (*convertArray)(const void *in, std::size_t stride, std::size_t n, void *out);
 };
 
@@ -57,7 +57,8 @@
     virtual TranslatedIndexBuffer *createIndexBuffer(std::size_t size) = 0;
     virtual FormatConverter getFormatConverter(GLenum type, std::size_t size, bool normalize) = 0;
 
-    virtual GLenum preDraw(const TranslatedAttribute *attributes) = 0;
+    virtual GLenum setupIndicesPreDraw(const TranslatedIndexData &indexInfo) = 0;
+    virtual GLenum setupAttributesPreDraw(const TranslatedAttribute *attributes) = 0;
 };
 
 class TranslatedBuffer
diff --git a/src/libGLESv2/geometry/dx9.cpp b/src/libGLESv2/geometry/dx9.cpp
index 73bab0e..c6681e9 100644
--- a/src/libGLESv2/geometry/dx9.cpp
+++ b/src/libGLESv2/geometry/dx9.cpp
@@ -17,6 +17,7 @@
 #include "common/debug.h"
 
 #include "geometry/vertexconversion.h"
+#include "geometry/IndexDataManager.h"
 
 namespace
 {
@@ -179,7 +180,13 @@
     return ib ? static_cast<Dx9IndexBuffer*>(ib)->getBuffer() : NULL;
 }
 
-GLenum Dx9BackEnd::preDraw(const TranslatedAttribute *attributes)
+GLenum Dx9BackEnd::setupIndicesPreDraw(const TranslatedIndexData &indexInfo)
+{
+    mDevice->SetIndices(getDxBuffer(indexInfo.buffer));
+    return GL_NO_ERROR;
+}
+
+GLenum Dx9BackEnd::setupAttributesPreDraw(const TranslatedAttribute *attributes)
 {
     HRESULT hr;
 
diff --git a/src/libGLESv2/geometry/dx9.h b/src/libGLESv2/geometry/dx9.h
index a1eeeb5..3e49672 100644
--- a/src/libGLESv2/geometry/dx9.h
+++ b/src/libGLESv2/geometry/dx9.h
@@ -27,7 +27,8 @@
     virtual TranslatedIndexBuffer *createIndexBuffer(std::size_t size);
     virtual FormatConverter getFormatConverter(GLenum type, std::size_t size, bool normalize);
 
-    virtual GLenum preDraw(const TranslatedAttribute *attributes);
+    virtual GLenum setupIndicesPreDraw(const TranslatedIndexData &indexInfo);
+    virtual GLenum setupAttributesPreDraw(const TranslatedAttribute *attributes);
 
   private:
     IDirect3DDevice9 *mDevice;
diff --git a/src/libGLESv2/geometry/vertexconversion.h b/src/libGLESv2/geometry/vertexconversion.h
index 5bbb06f..c299021 100644
--- a/src/libGLESv2/geometry/vertexconversion.h
+++ b/src/libGLESv2/geometry/vertexconversion.h
@@ -180,12 +180,12 @@
         return convertArray(static_cast<const InputType*>(in), stride, n, static_cast<OutputType*>(out));
     }
 
-    static void convertIndexed(const InputType *in, std::size_t stride, std::size_t n, const Index *indices, OutputType *out)
+    static void convertIndexed(const InputType *in, std::size_t stride, Index minIndex, std::size_t n, const Index *indices, OutputType *out)
     {
         for (std::size_t i = 0; i < n; i++)
         {
-            const InputType *ein = pointerAddBytes(in, indices[i] * stride);
-            OutputType *eout = pointerAddBytes(out, indices[i] * finalSize);
+            const InputType *ein = pointerAddBytes(in, (indices[i] - minIndex) * stride);
+            OutputType *eout = pointerAddBytes(out, (indices[i] - minIndex) * finalSize);
 
             copyComponent(eout, ein, 0, static_cast<OutputType>(DefaultValueRule::zero()));
             copyComponent(eout, ein, 1, static_cast<OutputType>(DefaultValueRule::zero()));
@@ -194,9 +194,9 @@
         }
     }
 
-    static void convertIndexed(const void *in, std::size_t stride, std::size_t n, const Index *indices, void *out)
+    static void convertIndexed(const void *in, std::size_t stride, Index minIndex, std::size_t n, const Index *indices, void *out)
     {
-        convertIndexed(static_cast<const InputType*>(in), stride, n, indices, static_cast<OutputType*>(out));
+        convertIndexed(static_cast<const InputType*>(in), stride, minIndex, n, indices, static_cast<OutputType*>(out));
     }
 
   private:
diff --git a/src/libGLESv2/libGLESv2.vcproj b/src/libGLESv2/libGLESv2.vcproj
index 2c75ee0..9c552d9 100644
--- a/src/libGLESv2/libGLESv2.vcproj
+++ b/src/libGLESv2/libGLESv2.vcproj
@@ -238,6 +238,10 @@
 					>

 				</File>

 				<File

+					RelativePath=".\geometry\IndexDataManager.cpp"

+					>

+				</File>

+				<File

 					RelativePath=".\geometry\VertexDataManager.cpp"

 					>

 				</File>

@@ -316,6 +320,10 @@
 					>

 				</File>

 				<File

+					RelativePath=".\geometry\IndexDataManager.h"

+					>

+				</File>

+				<File

 					RelativePath=".\geometry\vertexconversion.h"

 					>

 				</File>