Avoid D3D11 primitive restart index using 32-bit indices

D3D11 interprets an index value of 0xFFFF in a 16-bit index buffer as a
primitive restart marker. This behavior can't be toggled off.
http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx
We work around it by converting to 32-bit indices.

BUG=angle:708

Change-Id: Ibc92d6ba98e5f11a98d76cae14f90ca050a19964
Reviewed-on: https://chromium-review.googlesource.com/211426
Reviewed-by: Shannon Woods <shannonwoods@chromium.org>
Tested-by: Nicolas Capens <capn@chromium.org>
diff --git a/src/libGLESv2/renderer/IndexDataManager.cpp b/src/libGLESv2/renderer/IndexDataManager.cpp
index b8c7fa9..fdd7989 100644
--- a/src/libGLESv2/renderer/IndexDataManager.cpp
+++ b/src/libGLESv2/renderer/IndexDataManager.cpp
@@ -1,6 +1,6 @@
 #include "precompiled.h"
 //
-// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+// Copyright (c) 2002-2014 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.
 //
@@ -15,6 +15,7 @@
 #include "libGLESv2/main.h"
 #include "libGLESv2/formatutils.h"
 #include "libGLESv2/renderer/IndexBuffer.h"
+#include "libGLESv2/renderer/Renderer.h"
 
 namespace rx
 {
@@ -54,10 +55,11 @@
     delete mCountingBuffer;
 }
 
-static void convertIndices(GLenum type, const void *input, GLsizei count, void *output)
+static void convertIndices(GLenum sourceType, GLenum destinationType, const void *input, GLsizei count, void *output)
 {
-    if (type == GL_UNSIGNED_BYTE)
+    if (sourceType == GL_UNSIGNED_BYTE)
     {
+        ASSERT(destinationType == GL_UNSIGNED_SHORT);
         const GLubyte *in = static_cast<const GLubyte*>(input);
         GLushort *out = static_cast<GLushort*>(output);
 
@@ -66,13 +68,28 @@
             out[i] = in[i];
         }
     }
-    else if (type == GL_UNSIGNED_INT)
+    else if (sourceType == GL_UNSIGNED_INT)
     {
+        ASSERT(destinationType == GL_UNSIGNED_INT);
         memcpy(output, input, count * sizeof(GLuint));
     }
-    else if (type == GL_UNSIGNED_SHORT)
+    else if (sourceType == GL_UNSIGNED_SHORT)
     {
-        memcpy(output, input, count * sizeof(GLushort));
+        if (destinationType == GL_UNSIGNED_SHORT)
+        {
+            memcpy(output, input, count * sizeof(GLushort));
+        }
+        else if (destinationType == GL_UNSIGNED_INT)
+        {
+            const GLushort *in = static_cast<const GLushort*>(input);
+            GLuint *out = static_cast<GLuint*>(output);
+
+            for (GLsizei i = 0; i < count; i++)
+            {
+                out[i] = in[i];
+            }
+        }
+        else UNREACHABLE();
     }
     else UNREACHABLE();
 }
@@ -155,21 +172,18 @@
         indices = static_cast<const GLubyte*>(storage->getData()) + offset;
     }
 
-    StreamingIndexBufferInterface *streamingBuffer = (type == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
-
     StaticIndexBufferInterface *staticBuffer = buffer ? buffer->getStaticIndexBuffer() : NULL;
-    IndexBufferInterface *indexBuffer = streamingBuffer;
+    IndexBufferInterface *indexBuffer = NULL;
     bool directStorage = alignedOffset && storage && storage->supportsDirectBinding() &&
                          destinationIndexType == type;
     unsigned int streamOffset = 0;
 
     if (directStorage)
     {
-        indexBuffer = streamingBuffer;
         streamOffset = offset;
 
         if (!buffer->getIndexRangeCache()->findRange(type, offset, count, &translated->minIndex,
-                                                     &translated->maxIndex, NULL))
+            &translated->maxIndex, NULL))
         {
             computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
             buffer->getIndexRangeCache()->addRange(type, offset, count, translated->minIndex,
@@ -191,6 +205,22 @@
     }
     else
     {
+        computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
+    }
+
+    // Avoid D3D11's primitive restart index value
+    // see http://msdn.microsoft.com/en-us/library/windows/desktop/bb205124(v=vs.85).aspx
+    if (translated->maxIndex == 0xFFFF && type == GL_UNSIGNED_SHORT && mRenderer->getMajorShaderModel() > 3)
+    {
+        destinationIndexType = GL_UNSIGNED_INT;
+        directStorage = false;
+        indexBuffer = NULL;
+    }
+
+    if (!directStorage && !indexBuffer)
+    {
+        indexBuffer = (destinationIndexType == GL_UNSIGNED_INT) ? mStreamingBufferInt : mStreamingBufferShort;
+
         unsigned int convertCount = count;
 
         if (staticBuffer)
@@ -234,7 +264,7 @@
             return GL_OUT_OF_MEMORY;
         }
 
-        convertIndices(type, staticBuffer ? storage->getData() : indices, convertCount, output);
+        convertIndices(type, destinationIndexType, staticBuffer ? storage->getData() : indices, convertCount, output);
 
         if (!indexBuffer->unmapBuffer())
         {
@@ -242,8 +272,6 @@
             return GL_OUT_OF_MEMORY;
         }
 
-        computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
-
         if (staticBuffer)
         {
             streamOffset = (offset / gl::GetTypeBytes(type)) * gl::GetTypeBytes(destinationIndexType);
@@ -253,10 +281,11 @@
     }
 
     translated->storage = directStorage ? storage : NULL;
-    translated->indexBuffer = indexBuffer->getIndexBuffer();
+    translated->indexBuffer = indexBuffer ? indexBuffer->getIndexBuffer() : NULL;
     translated->serial = directStorage ? storage->getSerial() : indexBuffer->getSerial();
     translated->startIndex = streamOffset / gl::GetTypeBytes(destinationIndexType);
     translated->startOffset = streamOffset;
+    translated->indexType = destinationIndexType;
 
     if (buffer)
     {
diff --git a/src/libGLESv2/renderer/IndexDataManager.h b/src/libGLESv2/renderer/IndexDataManager.h
index 0e77c81..70c7bf3 100644
--- a/src/libGLESv2/renderer/IndexDataManager.h
+++ b/src/libGLESv2/renderer/IndexDataManager.h
@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+// Copyright (c) 2002-2014 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.
 //
@@ -39,6 +39,7 @@
 
     IndexBuffer *indexBuffer;
     BufferStorage *storage;
+    GLenum indexType;
     unsigned int serial;
 };
 
diff --git a/src/libGLESv2/renderer/d3d11/Renderer11.cpp b/src/libGLESv2/renderer/d3d11/Renderer11.cpp
index c0ad111..f02e334 100644
--- a/src/libGLESv2/renderer/d3d11/Renderer11.cpp
+++ b/src/libGLESv2/renderer/d3d11/Renderer11.cpp
@@ -1137,10 +1137,8 @@
 
     if (err == GL_NO_ERROR)
     {
-        IndexBuffer11* indexBuffer = IndexBuffer11::makeIndexBuffer11(indexInfo->indexBuffer);
-
         ID3D11Buffer *buffer = NULL;
-        DXGI_FORMAT bufferFormat = indexBuffer->getIndexFormat();
+        DXGI_FORMAT bufferFormat = (indexInfo->indexType == GL_UNSIGNED_INT) ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT;
 
         if (indexInfo->storage)
         {
@@ -1149,6 +1147,7 @@
         }
         else
         {
+            IndexBuffer11* indexBuffer = IndexBuffer11::makeIndexBuffer11(indexInfo->indexBuffer);
             buffer = indexBuffer->getBuffer();
         }
 
@@ -1372,7 +1371,7 @@
 
     if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || mAppliedIBOffset != indexBufferOffset)
     {
-        mDeviceContext->IASetIndexBuffer(indexBuffer->getBuffer(), indexBuffer->getIndexFormat(), indexBufferOffset);
+        mDeviceContext->IASetIndexBuffer(d3dIndexBuffer, indexFormat, indexBufferOffset);
         mAppliedIB = d3dIndexBuffer;
         mAppliedIBFormat = indexFormat;
         mAppliedIBOffset = indexBufferOffset;
@@ -1483,7 +1482,7 @@
 
     if (mAppliedIB != d3dIndexBuffer || mAppliedIBFormat != indexFormat || mAppliedIBOffset != indexBufferOffset)
     {
-        mDeviceContext->IASetIndexBuffer(indexBuffer->getBuffer(), indexBuffer->getIndexFormat(), indexBufferOffset);
+        mDeviceContext->IASetIndexBuffer(d3dIndexBuffer, indexFormat, indexBufferOffset);
         mAppliedIB = d3dIndexBuffer;
         mAppliedIBFormat = indexFormat;
         mAppliedIBOffset = indexBufferOffset;