Primitive restart implementation

Wrote a new version of primitive restart
entirely handled by the index data manager.

Passes all primitive_restart dEQP tests

Change-Id: I0ce28764a33300a6625f1c369f95d8d178e1e3c0
Reviewed-on: https://swiftshader-review.googlesource.com/10951
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index 68129b6..29c488c 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -2972,7 +2972,7 @@
 // Applies the indices and element array bindings
 GLenum Context::applyIndexBuffer(const void *indices, GLuint start, GLuint end, GLsizei count, GLenum mode, GLenum type, TranslatedIndexData *indexInfo)
 {
-	GLenum err = mIndexDataManager->prepareIndexData(type, start, end, count, getCurrentVertexArray()->getElementArrayBuffer(), indices, indexInfo);
+	GLenum err = mIndexDataManager->prepareIndexData(mode, type, start, end, count, getCurrentVertexArray()->getElementArrayBuffer(), indices, indexInfo, isPrimitiveRestartFixedIndexEnabled());
 
 	if(err == GL_NO_ERROR)
 	{
@@ -3483,11 +3483,29 @@
 		return error(GL_INVALID_OPERATION);
 	}
 
+	GLenum internalMode = mode;
+	if(isPrimitiveRestartFixedIndexEnabled())
+	{
+		switch(mode)
+		{
+		case GL_TRIANGLE_FAN:
+		case GL_TRIANGLE_STRIP:
+			internalMode = GL_TRIANGLES;
+			break;
+		case GL_LINE_LOOP:
+		case GL_LINE_STRIP:
+			internalMode = GL_LINES;
+			break;
+		default:
+			break;
+		}
+	}
+
 	sw::DrawType primitiveType;
 	int primitiveCount;
 	int verticesPerPrimitive;
 
-	if(!es2sw::ConvertPrimitiveType(mode, count, type, primitiveType, primitiveCount, verticesPerPrimitive))
+	if(!es2sw::ConvertPrimitiveType(internalMode, count, type, primitiveType, primitiveCount, verticesPerPrimitive))
 		return error(GL_INVALID_ENUM);
 
 	if(primitiveCount <= 0)
@@ -3500,19 +3518,19 @@
 		return;
 	}
 
-	applyState(mode);
+	TranslatedIndexData indexInfo(primitiveCount);
+	GLenum err = applyIndexBuffer(indices, start, end, count, mode, type, &indexInfo);
+	if(err != GL_NO_ERROR)
+	{
+		return error(err);
+	}
+
+	applyState(internalMode);
 
 	for(int i = 0; i < instanceCount; ++i)
 	{
 		device->setInstanceID(i);
 
-		TranslatedIndexData indexInfo;
-		GLenum err = applyIndexBuffer(indices, start, end, count, mode, type, &indexInfo);
-		if(err != GL_NO_ERROR)
-		{
-			return error(err);
-		}
-
 		GLsizei vertexCount = indexInfo.maxIndex - indexInfo.minIndex + 1;
 		err = applyVertexBuffer(-(int)indexInfo.minIndex, indexInfo.minIndex, vertexCount, i);
 		if(err != GL_NO_ERROR)
@@ -3529,13 +3547,13 @@
 		}
 
 		TransformFeedback* transformFeedback = getTransformFeedback();
-		if(!cullSkipsDraw(mode) || (transformFeedback->isActive() && !transformFeedback->isPaused()))
+		if(!cullSkipsDraw(internalMode) || (transformFeedback->isActive() && !transformFeedback->isPaused()))
 		{
-			device->drawIndexedPrimitive(primitiveType, indexInfo.indexOffset, primitiveCount);
+			device->drawIndexedPrimitive(primitiveType, indexInfo.indexOffset, indexInfo.primitiveCount);
 		}
 		if(transformFeedback)
 		{
-			transformFeedback->addVertexOffset(primitiveCount * verticesPerPrimitive);
+			transformFeedback->addVertexOffset(indexInfo.primitiveCount * verticesPerPrimitive);
 		}
 	}
 }
diff --git a/src/OpenGL/libGLESv2/IndexDataManager.cpp b/src/OpenGL/libGLESv2/IndexDataManager.cpp
index 81d1c6a..57d7f4b 100644
--- a/src/OpenGL/libGLESv2/IndexDataManager.cpp
+++ b/src/OpenGL/libGLESv2/IndexDataManager.cpp
@@ -63,37 +63,213 @@
 	else UNREACHABLE(type);
 }
 
-template<class IndexType>
-void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
+inline GLsizei getNumIndices(const std::vector<GLsizei>& restartIndices, size_t i, GLsizei count)
 {
-	*minIndex = indices[0];
-	*maxIndex = indices[0];
+	return (i == 0) ? restartIndices[0] : ((i == restartIndices.size()) ? (count - restartIndices[i - 1] - 1) : (restartIndices[i] - restartIndices[i - 1] - 1));
+}
+
+void copyIndices(GLenum mode, GLenum type, const std::vector<GLsizei>& restartIndices, const void *input, GLsizei count, void* output)
+{
+	size_t bytesPerIndex = 0;
+	const unsigned char* inPtr = static_cast<const unsigned char*>(input);
+	unsigned char* outPtr = static_cast<unsigned char*>(output);
+	switch(type)
+	{
+	case GL_UNSIGNED_BYTE:
+		bytesPerIndex = sizeof(GLubyte);
+		break;
+	case GL_UNSIGNED_INT:
+		bytesPerIndex = sizeof(GLuint);
+		break;
+	case GL_UNSIGNED_SHORT:
+		bytesPerIndex = sizeof(GLushort);
+		break;
+	default:
+		UNREACHABLE(type);
+	}
+
+	size_t numRestarts = restartIndices.size();
+	switch(mode)
+	{
+	case GL_TRIANGLES:
+	case GL_LINES:
+	case GL_POINTS:
+	{
+		GLsizei verticesPerPrimitive = (mode == GL_TRIANGLES) ? 3 : ((mode == GL_LINES) ? 2 : 1);
+		for(size_t i = 0; i <= numRestarts; ++i)
+		{
+			GLsizei numIndices = getNumIndices(restartIndices, i, count);
+			size_t numBytes = (numIndices / verticesPerPrimitive) * verticesPerPrimitive * bytesPerIndex;
+			if(numBytes > 0)
+			{
+				memcpy(outPtr, inPtr, numBytes);
+				outPtr += numBytes;
+			}
+			inPtr += (numIndices + 1) * bytesPerIndex;
+		}
+	}
+		break;
+	case GL_TRIANGLE_FAN:
+		for(size_t i = 0; i <= numRestarts; ++i)
+		{
+			GLsizei numIndices = getNumIndices(restartIndices, i, count);
+			GLsizei numTriangles = (numIndices - 2);
+			for(GLsizei tri = 0; tri < numTriangles; ++tri)
+			{
+				memcpy(outPtr, inPtr, bytesPerIndex);
+				outPtr += bytesPerIndex;
+				memcpy(outPtr, inPtr + ((tri + 1) * bytesPerIndex), bytesPerIndex + bytesPerIndex);
+				outPtr += bytesPerIndex + bytesPerIndex;
+			}
+			inPtr += (numIndices + 1) * bytesPerIndex;
+		}
+		break;
+	case GL_TRIANGLE_STRIP:
+		for(size_t i = 0; i <= numRestarts; ++i)
+		{
+			GLsizei numIndices = getNumIndices(restartIndices, i, count);
+			GLsizei numTriangles = (numIndices - 2);
+			for(GLsizei tri = 0; tri < numTriangles; ++tri)
+			{
+				if(tri & 1) // Reverse odd triangles
+				{
+					memcpy(outPtr, inPtr + ((tri + 1) * bytesPerIndex), bytesPerIndex);
+					outPtr += bytesPerIndex;
+					memcpy(outPtr, inPtr + ((tri + 0) * bytesPerIndex), bytesPerIndex);
+					outPtr += bytesPerIndex;
+					memcpy(outPtr, inPtr + ((tri + 2) * bytesPerIndex), bytesPerIndex);
+					outPtr += bytesPerIndex;
+				}
+				else
+				{
+					size_t numBytes = 3 * bytesPerIndex;
+					memcpy(outPtr, inPtr + (tri * bytesPerIndex), numBytes);
+					outPtr += numBytes;
+				}
+			}
+			inPtr += (numIndices + 1) * bytesPerIndex;
+		}
+		break;
+	case GL_LINE_LOOP:
+		for(size_t i = 0; i <= numRestarts; ++i)
+		{
+			GLsizei numIndices = getNumIndices(restartIndices, i, count);
+			if(numIndices >= 2)
+			{
+				GLsizei numLines = numIndices;
+				memcpy(outPtr, inPtr + (numIndices - 1) * bytesPerIndex, bytesPerIndex); // Last vertex
+				outPtr += bytesPerIndex;
+				memcpy(outPtr, inPtr, bytesPerIndex); // First vertex
+				outPtr += bytesPerIndex;
+				size_t bytesPerLine = 2 * bytesPerIndex;
+				for(GLsizei tri = 0; tri < (numLines - 1); ++tri)
+				{
+					memcpy(outPtr, inPtr + tri * bytesPerIndex, bytesPerLine);
+					outPtr += bytesPerLine;
+				}
+			}
+			inPtr += (numIndices + 1) * bytesPerIndex;
+		}
+		break;
+	case GL_LINE_STRIP:
+		for(size_t i = 0; i <= numRestarts; ++i)
+		{
+			GLsizei numIndices = getNumIndices(restartIndices, i, count);
+			GLsizei numLines = numIndices - 1;
+			size_t bytesPerLine = 2 * bytesPerIndex;
+			for(GLsizei tri = 0; tri < numLines; ++tri)
+			{
+				memcpy(outPtr, inPtr + tri * bytesPerIndex, bytesPerLine);
+				outPtr += bytesPerLine;
+			}
+			inPtr += (numIndices + 1) * bytesPerIndex;
+		}
+		break;
+	default:
+		UNREACHABLE(mode);
+		break;
+	}
+}
+
+template<class IndexType>
+void computeRange(const IndexType *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex, std::vector<GLsizei>* restartIndices)
+{
+	*maxIndex = 0;
+	*minIndex = MAX_ELEMENTS_INDICES;
 
 	for(GLsizei i = 0; i < count; i++)
 	{
+		if(restartIndices && indices[i] == IndexType(-1))
+		{
+			restartIndices->push_back(i);
+			continue;
+		}
 		if(*minIndex > indices[i]) *minIndex = indices[i];
 		if(*maxIndex < indices[i]) *maxIndex = indices[i];
 	}
 }
 
-void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex)
+void computeRange(GLenum type, const void *indices, GLsizei count, GLuint *minIndex, GLuint *maxIndex, std::vector<GLsizei>* restartIndices)
 {
 	if(type == GL_UNSIGNED_BYTE)
 	{
-		computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex);
+		computeRange(static_cast<const GLubyte*>(indices), count, minIndex, maxIndex, restartIndices);
 	}
 	else if(type == GL_UNSIGNED_INT)
 	{
-		computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex);
+		computeRange(static_cast<const GLuint*>(indices), count, minIndex, maxIndex, restartIndices);
 	}
 	else if(type == GL_UNSIGNED_SHORT)
 	{
-		computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex);
+		computeRange(static_cast<const GLushort*>(indices), count, minIndex, maxIndex, restartIndices);
 	}
 	else UNREACHABLE(type);
 }
 
-GLenum IndexDataManager::prepareIndexData(GLenum type, GLuint start, GLuint end, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated)
+int recomputePrimitiveCount(GLenum mode, GLsizei count, const std::vector<GLsizei>& restartIndices, unsigned int* primitiveCount)
+{
+	size_t numRestarts = restartIndices.size();
+	*primitiveCount = 0;
+
+	unsigned int countOffset = 0;
+	unsigned int vertexPerPrimitive = 0;
+
+	switch(mode)
+	{
+	case GL_TRIANGLES: // 3 vertex per primitive
+		++vertexPerPrimitive;
+	case GL_LINES: //  2 vertex per primitive
+		vertexPerPrimitive += 2;
+		for(size_t i = 0; i <= numRestarts; ++i)
+		{
+			unsigned int nbIndices = getNumIndices(restartIndices, i, count);
+			*primitiveCount += nbIndices / vertexPerPrimitive;
+		}
+		return vertexPerPrimitive;
+	case GL_TRIANGLE_FAN:
+	case GL_TRIANGLE_STRIP: // (N - 2) polygons, 3 vertex per primitive
+		++vertexPerPrimitive;
+		--countOffset;
+	case GL_LINE_STRIP: // (N - 1) polygons, 2 vertex per primitive
+		--countOffset;
+	case GL_LINE_LOOP: // N polygons, 2 vertex per primitive
+		vertexPerPrimitive += 2;
+		for(size_t i = 0; i <= numRestarts; ++i)
+		{
+			unsigned int nbIndices = getNumIndices(restartIndices, i, count);
+			*primitiveCount += (nbIndices >= vertexPerPrimitive) ? (nbIndices + countOffset) : 0;
+		}
+		return vertexPerPrimitive;
+	case GL_POINTS:
+		*primitiveCount = count - restartIndices.size();
+		return 1;
+	default:
+		UNREACHABLE(mode);
+		return -1;
+	}
+}
+
+GLenum IndexDataManager::prepareIndexData(GLenum mode, GLenum type, GLuint start, GLuint end, GLsizei count, Buffer *buffer, const void *indices, TranslatedIndexData *translated, bool primitiveRestart)
 {
 	if(!mStreamingBuffer)
 	{
@@ -112,14 +288,49 @@
 		indices = static_cast<const GLubyte*>(buffer->data()) + offset;
 	}
 
+	std::vector<GLsizei>* restartIndices = primitiveRestart ? new std::vector<GLsizei>() : nullptr;
+	computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex, restartIndices);
+	if(restartIndices && restartIndices->empty())
+	{
+		delete restartIndices;
+		restartIndices = nullptr;
+	}
+
 	StreamingIndexBuffer *streamingBuffer = mStreamingBuffer;
 
 	sw::Resource *staticBuffer = buffer ? buffer->getResource() : NULL;
 
-	if(staticBuffer)
+	if(restartIndices)
 	{
-		computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
+		int vertexPerPrimitive = recomputePrimitiveCount(mode, count, *restartIndices, &translated->primitiveCount);
+		if(vertexPerPrimitive == -1)
+		{
+			delete restartIndices;
+			return GL_INVALID_ENUM;
+		}
 
+		size_t streamOffset = 0;
+		int convertCount = translated->primitiveCount * vertexPerPrimitive;
+
+		streamingBuffer->reserveSpace(convertCount * typeSize(type), type);
+		void *output = streamingBuffer->map(typeSize(type) * convertCount, &streamOffset);
+
+		if(output == NULL)
+		{
+			delete restartIndices;
+			ERR("Failed to map index buffer.");
+			return GL_OUT_OF_MEMORY;
+		}
+
+		copyIndices(mode, type, *restartIndices, staticBuffer ? buffer->data() : indices, count, output);
+		streamingBuffer->unmap();
+
+		translated->indexBuffer = streamingBuffer->getResource();
+		translated->indexOffset = static_cast<unsigned int>(streamOffset);
+		delete restartIndices;
+	}
+	else if(staticBuffer)
+	{
 		translated->indexBuffer = staticBuffer;
 		translated->indexOffset = static_cast<unsigned int>(offset);
 	}
@@ -137,11 +348,9 @@
 			return GL_OUT_OF_MEMORY;
 		}
 
-		copyIndices(type, staticBuffer ? buffer->data() : indices, convertCount, output);
+		copyIndices(type, indices, convertCount, output);
 		streamingBuffer->unmap();
 
-		computeRange(type, indices, count, &translated->minIndex, &translated->maxIndex);
-
 		translated->indexBuffer = streamingBuffer->getResource();
 		translated->indexOffset = static_cast<unsigned int>(streamOffset);
 	}
diff --git a/src/OpenGL/libGLESv2/IndexDataManager.h b/src/OpenGL/libGLESv2/IndexDataManager.h
index 0a5e398..1834fbc 100644
--- a/src/OpenGL/libGLESv2/IndexDataManager.h
+++ b/src/OpenGL/libGLESv2/IndexDataManager.h
@@ -27,9 +27,12 @@
 
 struct TranslatedIndexData
 {
+	TranslatedIndexData(unsigned int primitiveCount) : primitiveCount(primitiveCount) {}
+
 	unsigned int minIndex;
 	unsigned int maxIndex;
 	unsigned int indexOffset;
+	unsigned int primitiveCount;
 
 	sw::Resource *indexBuffer;
 };
@@ -58,7 +61,7 @@
 	IndexDataManager();
 	virtual ~IndexDataManager();
 
-	GLenum prepareIndexData(GLenum type, GLuint start, GLuint end, GLsizei count, Buffer *arrayElementBuffer, const void *indices, TranslatedIndexData *translated);
+	GLenum prepareIndexData(GLenum mode, GLenum type, GLuint start, GLuint end, GLsizei count, Buffer *arrayElementBuffer, const void *indices, TranslatedIndexData *translated, bool primitiveRestart);
 
 	static std::size_t typeSize(GLenum type);