Primitive Restart implementation

This cl is a very basic implementation of primitive restart
which records every segment of at least 1 primitive into a
vector of index buffers and primitive counts which are then
looped over at draw time. There's no caching involved, so
the primitive restart segments will get recomputed at every
draw, until a caching mechanism is introduced.

Tests: dEQP-VK.pipeline.input_assembly.primitive_restart.*

Bug b/118619338

Change-Id: If2f88ffb7971d1aa7d1451c7916f0ba65a2e09de
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31032
Tested-by: Alexis Hétu <sugoi@google.com>
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index f161e9e..c177533 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -455,6 +455,51 @@
 		return executionState.indexType == VK_INDEX_TYPE_UINT16 ? 2 : 4;
 	}
 
+	template<typename T>
+	void processPrimitiveRestart(T* indexBuffer,
+	                             uint32_t count,
+		                         GraphicsPipeline* pipeline,
+	                             std::vector<std::pair<uint32_t, void*>>& indexBuffers)
+	{
+		static const T RestartIndex = static_cast<T>(-1);
+		T* indexBufferStart = indexBuffer;
+		uint32_t vertexCount = 0;
+		for(uint32_t i = 0; i < count; i++)
+		{
+			if(indexBuffer[i] == RestartIndex)
+			{
+				// Record previous segment
+				if(vertexCount > 0)
+				{
+					uint32_t primitiveCount = pipeline->computePrimitiveCount(vertexCount);
+					if(primitiveCount > 0)
+					{
+						indexBuffers.push_back({ primitiveCount, indexBufferStart });
+					}
+				}
+				vertexCount = 0;
+			}
+			else
+			{
+				if(vertexCount == 0)
+				{
+					indexBufferStart = indexBuffer + i;
+				}
+				vertexCount++;
+			}
+		}
+
+		// Record last segment
+		if(vertexCount > 0)
+		{
+			uint32_t primitiveCount = pipeline->computePrimitiveCount(vertexCount);
+			if(primitiveCount > 0)
+			{
+				indexBuffers.push_back({ primitiveCount, indexBufferStart });
+			}
+		}
+	}
+
 	void draw(CommandBuffer::ExecutionState& executionState, bool indexed,
 			uint32_t count, uint32_t instanceCount, uint32_t first, int32_t vertexOffset, uint32_t firstInstance)
 	{
@@ -470,12 +515,6 @@
 		context.descriptorDynamicOffsets = pipelineState.descriptorDynamicOffsets;
 		context.pushConstants = executionState.pushConstants;
 
-		if(indexed)
-		{
-			context.indexBuffer = Cast(executionState.indexBufferBinding.buffer)->getOffsetPointer(
-					executionState.indexBufferBinding.offset + first * bytesPerIndex(executionState));
-		}
-
 		// Apply either pipeline state or dynamic state
 		executionState.renderer->setScissor(pipeline->hasDynamicState(VK_DYNAMIC_STATE_SCISSOR) ?
 		                                    executionState.dynamicState.scissor : pipeline->getScissor());
@@ -520,11 +559,46 @@
 		context.multiSampleMask = context.sampleMask & ((unsigned)0xFFFFFFFF >> (32 - context.sampleCount));
 		context.occlusionEnabled = executionState.renderer->hasQueryOfType(VK_QUERY_TYPE_OCCLUSION);
 
-		const uint32_t primitiveCount = pipeline->computePrimitiveCount(count);
+		std::vector<std::pair<uint32_t, void*>> indexBuffers;
+		if(indexed)
+		{
+			void* indexBuffer = Cast(executionState.indexBufferBinding.buffer)->getOffsetPointer(
+				executionState.indexBufferBinding.offset + first * bytesPerIndex(executionState));
+			if(pipeline->hasPrimitiveRestartEnable())
+			{
+				switch(executionState.indexType)
+				{
+				case VK_INDEX_TYPE_UINT16:
+					processPrimitiveRestart(static_cast<uint16_t*>(indexBuffer), count, pipeline, indexBuffers);
+					break;
+				case VK_INDEX_TYPE_UINT32:
+					processPrimitiveRestart(static_cast<uint32_t*>(indexBuffer), count, pipeline, indexBuffers);
+					break;
+				default:
+					UNIMPLEMENTED("executionState.indexType %d", int(executionState.indexType));
+				}
+			}
+			else
+			{
+				indexBuffers.push_back({ pipeline->computePrimitiveCount(count), indexBuffer });
+			}
+		}
+		else
+		{
+			indexBuffers.push_back({ pipeline->computePrimitiveCount(count), nullptr });
+		}
+
 		for(uint32_t instance = firstInstance; instance != firstInstance + instanceCount; instance++)
 		{
 			context.instanceID = instance;
-			executionState.renderer->draw(&context, executionState.indexType, primitiveCount, vertexOffset, executionState.fence);
+
+			for(auto indexBuffer : indexBuffers)
+			{
+				const uint32_t primitiveCount = indexBuffer.first;
+				context.indexBuffer = indexBuffer.second;
+				executionState.renderer->draw(&context, executionState.indexType, primitiveCount, vertexOffset, executionState.fence);
+			}
+
 			executionState.renderer->advanceInstanceAttributes(context.input);
 		}
 	}
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index 72ead29..62a36ce 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -301,12 +301,12 @@
 	}
 
 	const VkPipelineInputAssemblyStateCreateInfo* assemblyState = pCreateInfo->pInputAssemblyState;
-	if((assemblyState->flags != 0) ||
-	   (assemblyState->primitiveRestartEnable != 0))
+	if(assemblyState->flags != 0)
 	{
 		UNIMPLEMENTED("pCreateInfo->pInputAssemblyState settings");
 	}
 
+	primitiveRestartEnable = assemblyState->primitiveRestartEnable;
 	context.topology = assemblyState->topology;
 
 	const VkPipelineViewportStateCreateInfo* viewportState = pCreateInfo->pViewportState;
diff --git a/src/Vulkan/VkPipeline.hpp b/src/Vulkan/VkPipeline.hpp
index d14a547..0f07018 100644
--- a/src/Vulkan/VkPipeline.hpp
+++ b/src/Vulkan/VkPipeline.hpp
@@ -80,12 +80,14 @@
 	const VkViewport& getViewport() const;
 	const sw::Color<float>& getBlendConstants() const;
 	bool hasDynamicState(VkDynamicState dynamicState) const;
+	bool hasPrimitiveRestartEnable() const { return primitiveRestartEnable; }
 
 private:
 	sw::SpirvShader *vertexShader = nullptr;
 	sw::SpirvShader *fragmentShader = nullptr;
 
 	uint32_t dynamicStateFlags = 0;
+	bool primitiveRestartEnable = false;
 	sw::Context context;
 	VkRect2D scissor;
 	VkViewport viewport;