Point vertex processing optimization

Instead of packing each point in a triangle primitive,
tightly pack all point vertices and write them out 3
at a time in the output primitive. This should be
roughly twice as fast. Explanation:

Currently:
Vertices: 0 0 0 1 1 1 2 2 2 ...
Processing:
1) 0 0 0 1
2) 0 (cache hit)
3) 0 (cache hit)
2) 1 (cache hit)
3) 1 (cache hit)
4) 1 (cache hit)
5) 2 2 2 3
...
-> We processed 8 vertices to get points 0 1 2 3

New way:
1) 0 1 2 3 -> We processed 4 vertices to get points 0 1 2 3
2) 4 5 6 7

Will affect these tests once vertexPipelineStoresAndAtomics
is enabled:
dEQP-VK.glsl.atomic_operations.*

Note that these tests are affected because they wrongly assume
vertices won't be processed more than once. These tests should
still get fixed.

Bug b/140294254

Change-Id: Idb21085838317db7b7a6630a18de4d7284534429
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/36349
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Device/Renderer.cpp b/src/Device/Renderer.cpp
index 854c935..8b0ab36 100644
--- a/src/Device/Renderer.cpp
+++ b/src/Device/Renderer.cpp
@@ -53,13 +53,17 @@
 		case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
 		{
 			auto index = start;
+			auto pointBatch = &(batch[0][0]);
 			for(unsigned int i = 0; i < triangleCount; i++)
 			{
-				batch[i][0] = indices[index];
-				batch[i][1] = indices[index];
-				batch[i][2] = indices[index];
+				*pointBatch++ = indices[index++];
+			}
 
-				index += 1;
+			// Repeat the last index to allow for SIMD width overrun.
+			index--;
+			for(unsigned int i = 0; i < 3; i++)
+			{
+				*pointBatch++ = indices[index];
 			}
 			break;
 		}
@@ -496,7 +500,8 @@
 
 		auto& vertexTask = batch->vertexTask;
 		vertexTask.primitiveStart = batch->firstPrimitive;
-		vertexTask.vertexCount = batch->numPrimitives * 3;
+		// We're only using batch compaction for points, not lines
+		vertexTask.vertexCount = batch->numPrimitives * ((draw->topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST) ? 1 : 3);
 		if (vertexTask.vertexCache.drawCall != draw->id)
 		{
 			vertexTask.vertexCache.clear();
@@ -590,10 +595,14 @@
 			}
 		}
 
-		// Repeat the last index to allow for SIMD width overrun.
-		triangleIndicesOut[triangleCount][0] = triangleIndicesOut[triangleCount - 1][2];
-		triangleIndicesOut[triangleCount][1] = triangleIndicesOut[triangleCount - 1][2];
-		triangleIndicesOut[triangleCount][2] = triangleIndicesOut[triangleCount - 1][2];
+		// setBatchIndices() takes care of the point case, since it's different due to the compaction
+		if (topology != VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
+		{
+			// Repeat the last index to allow for SIMD width overrun.
+			triangleIndicesOut[triangleCount][0] = triangleIndicesOut[triangleCount - 1][2];
+			triangleIndicesOut[triangleCount][1] = triangleIndicesOut[triangleCount - 1][2];
+			triangleIndicesOut[triangleCount][2] = triangleIndicesOut[triangleCount - 1][2];
+		}
 	}
 
 	int DrawCall::setupSolidTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
diff --git a/src/Device/VertexProcessor.cpp b/src/Device/VertexProcessor.cpp
index c6e5c13..72e4cf4 100644
--- a/src/Device/VertexProcessor.cpp
+++ b/src/Device/VertexProcessor.cpp
@@ -78,6 +78,7 @@
 		State state;
 
 		state.shaderID = context->vertexShader->getSerialID();
+		state.isPoint = context->topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
 
 		for(int i = 0; i < MAX_INTERFACE_COMPONENTS / 4; i++)
 		{
diff --git a/src/Device/VertexProcessor.hpp b/src/Device/VertexProcessor.hpp
index 66ba01d..247e8a3 100644
--- a/src/Device/VertexProcessor.hpp
+++ b/src/Device/VertexProcessor.hpp
@@ -75,6 +75,7 @@
 			};
 
 			Input input[MAX_INTERFACE_COMPONENTS / 4];
+			bool isPoint : 1;
 		};
 
 		struct State : States
diff --git a/src/Pipeline/VertexRoutine.cpp b/src/Pipeline/VertexRoutine.cpp
index 8ada5d4..e43e96b 100644
--- a/src/Pipeline/VertexRoutine.cpp
+++ b/src/Pipeline/VertexRoutine.cpp
@@ -67,9 +67,14 @@
 			}
 
 			Pointer<Byte> cacheEntry = vertexCache + cacheIndex * UInt((int)sizeof(Vertex));
-			writeVertex(vertex, cacheEntry);
 
-			vertex += sizeof(Vertex);
+			// For points, vertexCount is 1 per primitive, so duplicate vertex for all 3 vertices of the primitive
+			for(int i = 0; i < (state.isPoint ? 3 : 1); i++)
+			{
+				writeVertex(vertex, cacheEntry);
+				vertex += sizeof(Vertex);
+			}
+
 			batch = Pointer<UInt>(Pointer<Byte>(batch) + sizeof(uint32_t));
 			vertexCount--;
 		}