Re-implement line loops by constructing a 'looping' index buffer, so it is compatible with instanced draws.

TRAC #19489
Signed-off-by: Daniel Koch
Author: Nicolas Capens

git-svn-id: https://angleproject.googlecode.com/svn/trunk@973 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index 096874e..9d63e6b 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -32,11 +32,6 @@
 #undef near
 #undef far
 
-namespace
-{
-    enum { CLOSING_INDEX_BUFFER_SIZE = 4096 };
-}
-
 namespace gl
 {
 Context::Context(const egl::Config *config, const gl::Context *shareContext, bool notifyResets, bool robustAccess) : mConfig(config)
@@ -153,7 +148,7 @@
     mVertexDataManager = NULL;
     mIndexDataManager = NULL;
     mBlit = NULL;
-    mClosingIB = NULL;
+    mLineLoopIB = NULL;
 
     mInvalidEnum = false;
     mInvalidValue = false;
@@ -244,7 +239,7 @@
     delete mVertexDataManager;
     delete mIndexDataManager;
     delete mBlit;
-    delete mClosingIB;
+    delete mLineLoopIB;
 
     if (mMaskedClearSavedState)
     {
@@ -2964,11 +2959,11 @@
     {
         mDisplay->startScene();
         
-        if (instances == 0)
+        if (mode == GL_LINE_LOOP)
         {
-            mDevice->DrawPrimitive(primitiveType, 0, primitiveCount);
+            drawLineLoop(count, GL_NONE, NULL, 0);
         }
-        else
+        else if (instances > 0)
         {
             StaticIndexBuffer *countingIB = mIndexDataManager->getCountingIndices(count);
             if (countingIB)
@@ -2987,10 +2982,9 @@
                 return error(GL_OUT_OF_MEMORY);
             }
         }
-
-        if (mode == GL_LINE_LOOP)   // Draw the last segment separately
+        else   // Regular case
         {
-            drawClosingLine(0, count - 1, 0);
+            mDevice->DrawPrimitive(primitiveType, 0, primitiveCount);
         }
     }
 }
@@ -3051,11 +3045,13 @@
     {
         mDisplay->startScene();
 
-        mDevice->DrawIndexedPrimitive(primitiveType, -(INT)indexInfo.minIndex, indexInfo.minIndex, vertexCount, indexInfo.startIndex, primitiveCount);
-
-        if (mode == GL_LINE_LOOP)   // Draw the last segment separately
+        if (mode == GL_LINE_LOOP)
         {
-            drawClosingLine(count, type, indices, indexInfo.minIndex);
+            drawLineLoop(count, type, indices, indexInfo.minIndex);   
+        }
+        else
+        {
+            mDevice->DrawIndexedPrimitive(primitiveType, -(INT)indexInfo.minIndex, indexInfo.minIndex, vertexCount, indexInfo.startIndex, primitiveCount);
         }
     }
 }
@@ -3066,98 +3062,149 @@
     mDisplay->sync(block);
 }
 
-void Context::drawClosingLine(unsigned int first, unsigned int last, int minIndex)
+void Context::drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex)
 {
-    bool succeeded = false;
-    UINT offset;
-
-    if (supports32bitIndices())
-    {
-        const int spaceNeeded = 2 * sizeof(unsigned int);
-
-        if (!mClosingIB)
-        {
-            mClosingIB = new StreamingIndexBuffer(mDevice, CLOSING_INDEX_BUFFER_SIZE, D3DFMT_INDEX32);
-        }
-
-        mClosingIB->reserveSpace(spaceNeeded, GL_UNSIGNED_INT);
-
-        unsigned int *data = static_cast<unsigned int*>(mClosingIB->map(spaceNeeded, &offset));
-        if (data)
-        {
-            data[0] = last;
-            data[1] = first;
-            mClosingIB->unmap();
-            offset /= 4;
-            succeeded = true;
-        }
-    }
-    else
-    {
-        const int spaceNeeded = 2 * sizeof(unsigned short);
-
-        if (!mClosingIB)
-        {
-            mClosingIB = new StreamingIndexBuffer(mDevice, CLOSING_INDEX_BUFFER_SIZE, D3DFMT_INDEX16);
-        }
-
-        mClosingIB->reserveSpace(spaceNeeded, GL_UNSIGNED_SHORT);
-
-        unsigned short *data = static_cast<unsigned short*>(mClosingIB->map(spaceNeeded, &offset));
-        if (data)
-        {
-            data[0] = last;
-            data[1] = first;
-            mClosingIB->unmap();
-            offset /= 2;
-            succeeded = true;
-        }
-    }
-    
-    if (succeeded)
-    {
-        mDevice->SetIndices(mClosingIB->getBuffer());
-        mAppliedIBSerial = mClosingIB->getSerial();
-
-        mDevice->DrawIndexedPrimitive(D3DPT_LINELIST, -minIndex, minIndex, last, offset, 1);
-    }
-    else
-    {
-        ERR("Could not create an index buffer for closing a line loop.");
-        error(GL_OUT_OF_MEMORY);
-    }
-}
-
-void Context::drawClosingLine(GLsizei count, GLenum type, const GLvoid *indices, int minIndex)
-{
-    unsigned int first = 0;
-    unsigned int last = 0;
-
-    if (mState.elementArrayBuffer.get())
+    // Get the raw indices for an indexed draw
+    if (type != GL_NONE && mState.elementArrayBuffer.get())
     {
         Buffer *indexBuffer = mState.elementArrayBuffer.get();
         intptr_t offset = reinterpret_cast<intptr_t>(indices);
         indices = static_cast<const GLubyte*>(indexBuffer->data()) + offset;
     }
 
-    switch (type)
-    {
-      case GL_UNSIGNED_BYTE:
-        first = static_cast<const GLubyte*>(indices)[0];
-        last = static_cast<const GLubyte*>(indices)[count - 1];
-        break;
-      case GL_UNSIGNED_SHORT:
-        first = static_cast<const GLushort*>(indices)[0];
-        last = static_cast<const GLushort*>(indices)[count - 1];
-        break;
-      case GL_UNSIGNED_INT:
-        first = static_cast<const GLuint*>(indices)[0];
-        last = static_cast<const GLuint*>(indices)[count - 1];
-        break;
-      default: UNREACHABLE();
-    }
+    UINT startIndex = 0;
+    bool succeeded = false;
 
-    drawClosingLine(first, last, minIndex);
+    if (supports32bitIndices())
+    {
+        const int spaceNeeded = (count + 1) * sizeof(unsigned int);
+
+        if (!mLineLoopIB)
+        {
+            mLineLoopIB = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX32);
+        }
+
+        if (mLineLoopIB)
+        {
+            mLineLoopIB->reserveSpace(spaceNeeded, GL_UNSIGNED_INT);
+
+            UINT offset = 0;
+            unsigned int *data = static_cast<unsigned int*>(mLineLoopIB->map(spaceNeeded, &offset));
+            startIndex = offset / 4;
+            
+            if (data)
+            {
+                switch (type)
+                {
+                  case GL_NONE:   // Non-indexed draw
+                    for (int i = 0; i < count; i++)
+                    {
+                        data[i] = i;
+                    }
+                    data[count] = 0;
+                    break;
+                  case GL_UNSIGNED_BYTE:
+                    for (int i = 0; i < count; i++)
+                    {
+                        data[i] = static_cast<const GLubyte*>(indices)[i];
+                    }
+                    data[count] = static_cast<const GLubyte*>(indices)[0];
+                    break;
+                  case GL_UNSIGNED_SHORT:
+                    for (int i = 0; i < count; i++)
+                    {
+                        data[i] = static_cast<const GLushort*>(indices)[i];
+                    }
+                    data[count] = static_cast<const GLushort*>(indices)[0];
+                    break;
+                  case GL_UNSIGNED_INT:
+                    for (int i = 0; i < count; i++)
+                    {
+                        data[i] = static_cast<const GLuint*>(indices)[i];
+                    }
+                    data[count] = static_cast<const GLuint*>(indices)[0];
+                    break;
+                  default: UNREACHABLE();
+                }
+
+                mLineLoopIB->unmap();
+                succeeded = true;
+            }
+        }
+    }
+    else
+    {
+        const int spaceNeeded = (count + 1) * sizeof(unsigned short);
+
+        if (!mLineLoopIB)
+        {
+            mLineLoopIB = new StreamingIndexBuffer(mDevice, INITIAL_INDEX_BUFFER_SIZE, D3DFMT_INDEX16);
+        }
+
+        if (mLineLoopIB)
+        {
+            mLineLoopIB->reserveSpace(spaceNeeded, GL_UNSIGNED_SHORT);
+
+            UINT offset = 0;
+            unsigned short *data = static_cast<unsigned short*>(mLineLoopIB->map(spaceNeeded, &offset));
+            startIndex = offset / 2;
+            
+            if (data)
+            {
+                switch (type)
+                {
+                  case GL_NONE:   // Non-indexed draw
+                    for (int i = 0; i < count; i++)
+                    {
+                        data[i] = i;
+                    }
+                    data[count] = 0;
+                    break;
+                  case GL_UNSIGNED_BYTE:
+                    for (int i = 0; i < count; i++)
+                    {
+                        data[i] = static_cast<const GLubyte*>(indices)[i];
+                    }
+                    data[count] = static_cast<const GLubyte*>(indices)[0];
+                    break;
+                  case GL_UNSIGNED_SHORT:
+                    for (int i = 0; i < count; i++)
+                    {
+                        data[i] = static_cast<const GLushort*>(indices)[i];
+                    }
+                    data[count] = static_cast<const GLushort*>(indices)[0];
+                    break;
+                  case GL_UNSIGNED_INT:
+                    for (int i = 0; i < count; i++)
+                    {
+                        data[i] = static_cast<const GLuint*>(indices)[i];
+                    }
+                    data[count] = static_cast<const GLuint*>(indices)[0];
+                    break;
+                  default: UNREACHABLE();
+                }
+
+                mLineLoopIB->unmap();
+                succeeded = true;
+            }
+        }
+    }
+    
+    if (succeeded)
+    {
+        if (mAppliedIBSerial != mLineLoopIB->getSerial())
+        {
+            mDevice->SetIndices(mLineLoopIB->getBuffer());
+            mAppliedIBSerial = mLineLoopIB->getSerial();
+        }
+
+        mDevice->DrawIndexedPrimitive(D3DPT_LINESTRIP, -minIndex, minIndex, count, startIndex, count);
+    }
+    else
+    {
+        ERR("Could not create a looping index buffer for GL_LINE_LOOP.");
+        return error(GL_OUT_OF_MEMORY);
+    }
 }
 
 void Context::recordInvalidEnum()
diff --git a/src/libGLESv2/Context.h b/src/libGLESv2/Context.h
index 58b7abc..e094f61 100644
--- a/src/libGLESv2/Context.h
+++ b/src/libGLESv2/Context.h
@@ -457,9 +457,7 @@
     void drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instances);
     void sync(bool block);   // flush/finish
 
-	// Draw the last segment of a line loop
-    void drawClosingLine(unsigned int first, unsigned int last, int minIndex);
-    void drawClosingLine(GLsizei count, GLenum type, const GLvoid *indices, int minIndex);
+    void drawLineLoop(GLsizei count, GLenum type, const GLvoid *indices, int minIndex);
 
     void recordInvalidEnum();
     void recordInvalidValue();
@@ -561,7 +559,7 @@
 
     Blit *mBlit;
 
-    StreamingIndexBuffer *mClosingIB;
+    StreamingIndexBuffer *mLineLoopIB;
     
     BindingPointer<Texture> mIncompleteTextures[TEXTURE_TYPE_COUNT];
 
diff --git a/src/libGLESv2/IndexDataManager.cpp b/src/libGLESv2/IndexDataManager.cpp
index e2e307d..3dc0aef 100644
--- a/src/libGLESv2/IndexDataManager.cpp
+++ b/src/libGLESv2/IndexDataManager.cpp
@@ -15,11 +15,6 @@
 #include "libGLESv2/mathutil.h"
 #include "libGLESv2/main.h"
 
-namespace
-{
-    enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
-}
-
 namespace gl
 {
 unsigned int IndexBuffer::mCurrentSerial = 1;
diff --git a/src/libGLESv2/IndexDataManager.h b/src/libGLESv2/IndexDataManager.h
index 3496d12..c1d4168 100644
--- a/src/libGLESv2/IndexDataManager.h
+++ b/src/libGLESv2/IndexDataManager.h
@@ -18,6 +18,11 @@
 
 #include "libGLESv2/Context.h"
 
+namespace
+{
+    enum { INITIAL_INDEX_BUFFER_SIZE = 4096 * sizeof(GLuint) };
+}
+
 namespace gl
 {