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);