Implement non-solid polygon rasterization

Implements Vulkan's 'fillModeNonSolid' feature.

Mostly a copy-and-paste of src/Renderer logic. It sets the batch size
for non-solid triangles to 1 so their edges or vertices can get
individually rasterized as lines or points.

Bug: b/139872671
Tests: dEQP-VK.rasterization.culling.*
Change-Id: Ifbae8dad32e8b43159fee459c75d7e593fc08041
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/35031
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index f517e6b..6825d01 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -29,7 +29,7 @@
 		init();
 	}
 
-	bool Context::isDrawPoint() const
+	bool Context::isDrawPoint(bool polygonModeAware) const
 	{
 		switch(topology)
 		{
@@ -37,46 +37,48 @@
 			return true;
 		case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
 		case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
+			return false;
 		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
 		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
 		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
-			break;
+			return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_POINT) : false;
 		default:
 			UNIMPLEMENTED("topology %d", int(topology));
 		}
 		return false;
 	}
 
-	bool Context::isDrawLine() const
+	bool Context::isDrawLine(bool polygonModeAware) const
 	{
 		switch(topology)
 		{
+		case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
+			return false;
 		case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
 		case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
 			return true;
-		case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
 		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
 		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
 		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
-			break;
+			return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_LINE) : false;
 		default:
 			UNIMPLEMENTED("topology %d", int(topology));
 		}
 		return false;
 	}
 
-	bool Context::isDrawTriangle() const
+	bool Context::isDrawTriangle(bool polygonModeAware) const
 	{
 		switch(topology)
 		{
-		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
-		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
-		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
-			return true;
 		case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
 		case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
 		case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
-			break;
+			return false;
+		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
+		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
+		case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
+			return polygonModeAware ? (polygonMode == VK_POLYGON_MODE_FILL) : true;
 		default:
 			UNIMPLEMENTED("topology %d", int(topology));
 		}
diff --git a/src/Device/Context.hpp b/src/Device/Context.hpp
index c86d956..610ec88 100644
--- a/src/Device/Context.hpp
+++ b/src/Device/Context.hpp
@@ -74,9 +74,9 @@
 
 		void init();
 
-		bool isDrawPoint() const;
-		bool isDrawLine() const;
-		bool isDrawTriangle() const;
+		bool isDrawPoint(bool polygonModeAware) const;
+		bool isDrawLine(bool polygonModeAware) const;
+		bool isDrawTriangle(bool polygonModeAware) const;
 
 		bool depthWriteActive() const;
 		bool depthBufferActive() const;
@@ -96,6 +96,7 @@
 		// Pixel processor states
 		VkCullModeFlags cullMode;
 		VkFrontFace frontFace;
+		VkPolygonMode polygonMode;
 
 		float depthBias;
 		float slopeDepthBias;
diff --git a/src/Device/Renderer.cpp b/src/Device/Renderer.cpp
index 3aa4c3e..debcc7c 100644
--- a/src/Device/Renderer.cpp
+++ b/src/Device/Renderer.cpp
@@ -206,16 +206,33 @@
 		}
 
 		DrawCall::SetupFunction setupPrimitives = nullptr;
+		unsigned int numPrimitivesPerBatch = MaxBatchSize / ms;
 
-		if(context->isDrawTriangle())
+		if(context->isDrawTriangle(false))
 		{
-			setupPrimitives = &DrawCall::setupTriangles;
+			switch(context->polygonMode)
+			{
+			case VK_POLYGON_MODE_FILL:
+				setupPrimitives = &DrawCall::setupSolidTriangles;
+				break;
+			case VK_POLYGON_MODE_LINE:
+				setupPrimitives = &DrawCall::setupWireframeTriangles;
+				numPrimitivesPerBatch = 1;
+				break;
+			case VK_POLYGON_MODE_POINT:
+				setupPrimitives = &DrawCall::setupPointTriangles;
+				numPrimitivesPerBatch = 1;
+				break;
+			default:
+				UNSUPPORTED("polygon mode: %d", int(context->polygonMode));
+				return;
+			}
 		}
-		else if(context->isDrawLine())
+		else if(context->isDrawLine(false))
 		{
 			setupPrimitives = &DrawCall::setupLines;
 		}
-		else   // Point draw
+		else  // Point primitive topology
 		{
 			setupPrimitives = &DrawCall::setupPoints;
 		}
@@ -224,7 +241,7 @@
 		draw->occlusionQuery = occlusionQuery;
 		draw->batchDataPool = &batchDataPool;
 		draw->numPrimitives = count;
-		draw->numPrimitivesPerBatch = MaxBatchSize / ms;
+		draw->numPrimitivesPerBatch = numPrimitivesPerBatch;
 		draw->numBatches = (count + draw->numPrimitivesPerBatch - 1) / draw->numPrimitivesPerBatch;
 		draw->topology = context->topology;
 		draw->indexType = indexType;
@@ -297,7 +314,7 @@
 			float F = viewport.maxDepth;
 			float Z = F - N;
 
-			if(context->isDrawTriangle())
+			if(context->isDrawTriangle(false))
 			{
 				N += context->depthBias;
 			}
@@ -567,7 +584,7 @@
 		triangleIndicesOut[triangleCount][2] = triangleIndicesOut[triangleCount - 1][2];
 	}
 
-	int DrawCall::setupTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
+	int DrawCall::setupSolidTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
 	{
 		auto &state = drawCall->setupState;
 		auto setupRoutine = drawCall->setupPointer;
@@ -607,6 +624,94 @@
 		return visible;
 	}
 
+	int DrawCall::setupWireframeTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
+	{
+		auto& state = drawCall->setupState;
+
+		int ms = state.multiSample;
+		int visible = 0;
+
+		const Vertex &v0 = triangles[0].v0;
+		const Vertex &v1 = triangles[0].v1;
+		const Vertex &v2 = triangles[0].v2;
+
+		float d = (v0.position.y * v1.position.x - v0.position.x * v1.position.y) * v2.position.w +
+		          (v0.position.x * v2.position.y - v0.position.y * v2.position.x) * v1.position.w +
+		          (v2.position.x * v1.position.y - v1.position.x * v2.position.y) * v0.position.w;
+
+		bool frontFacing = (state.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE) ? d > 0.0f : d < 0.0f;
+		if(state.cullMode & VK_CULL_MODE_FRONT_BIT)
+		{
+			if(frontFacing) return 0;
+		}
+		if(state.cullMode & VK_CULL_MODE_BACK_BIT)
+		{
+			if(!frontFacing) return 0;
+		}
+
+		// Copy attributes
+		triangles[1].v0 = v1;
+		triangles[1].v1 = v2;
+		triangles[2].v0 = v2;
+		triangles[2].v1 = v0;
+
+		for(int i = 0; i < 3; i++)
+		{
+			if(setupLine(*primitives, *triangles, *drawCall))
+			{
+				primitives += ms;
+				visible++;
+			}
+
+			triangles++;
+		}
+
+		return visible;
+	}
+
+	int DrawCall::setupPointTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
+	{
+		auto& state = drawCall->setupState;
+
+		int ms = state.multiSample;
+		int visible = 0;
+
+		const Vertex &v0 = triangles[0].v0;
+		const Vertex &v1 = triangles[0].v1;
+		const Vertex &v2 = triangles[0].v2;
+
+		float d = (v0.position.y * v1.position.x - v0.position.x * v1.position.y) * v2.position.w +
+		          (v0.position.x * v2.position.y - v0.position.y * v2.position.x) * v1.position.w +
+		          (v2.position.x * v1.position.y - v1.position.x * v2.position.y) * v0.position.w;
+
+		bool frontFacing = (state.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE) ? d > 0.0f : d < 0.0f;
+		if(state.cullMode & VK_CULL_MODE_FRONT_BIT)
+		{
+			if(frontFacing) return 0;
+		}
+		if(state.cullMode & VK_CULL_MODE_BACK_BIT)
+		{
+			if(!frontFacing) return 0;
+		}
+
+		// Copy attributes
+		triangles[1].v0 = v1;
+		triangles[2].v0 = v2;
+
+		for(int i = 0; i < 3; i++)
+		{
+			if(setupPoint(*primitives, *triangles, *drawCall))
+			{
+				primitives += ms;
+				visible++;
+			}
+
+			triangles++;
+		}
+
+		return visible;
+	}
+
 	int DrawCall::setupLines(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count)
 	{
 		auto &state = drawCall->setupState;
diff --git a/src/Device/Renderer.hpp b/src/Device/Renderer.hpp
index b8a378b..4b96471 100644
--- a/src/Device/Renderer.hpp
+++ b/src/Device/Renderer.hpp
@@ -179,7 +179,9 @@
 				unsigned int triangleCount,
 				VkPrimitiveTopology topology);
 
-		static int setupTriangles(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count);
+		static int setupSolidTriangles(Triangle* triangles, Primitive* primitives, const DrawCall* drawCall, int count);
+		static int setupWireframeTriangles(Triangle* triangles, Primitive* primitives, const DrawCall* drawCall, int count);
+		static int setupPointTriangles(Triangle* triangles, Primitive* primitives, const DrawCall* drawCall, int count);
 		static int setupLines(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count);
 		static int setupPoints(Triangle *triangles, Primitive *primitives, const DrawCall *drawCall, int count);
 
diff --git a/src/Device/SetupProcessor.cpp b/src/Device/SetupProcessor.cpp
index abf4c36..9984051 100644
--- a/src/Device/SetupProcessor.cpp
+++ b/src/Device/SetupProcessor.cpp
@@ -69,14 +69,14 @@
 
 		bool vPosZW = (context->pixelShader && context->pixelShader->hasBuiltinInput(spv::BuiltInFragCoord));
 
-		state.isDrawPoint = context->isDrawPoint();
-		state.isDrawLine = context->isDrawLine();
-		state.isDrawTriangle = context->isDrawTriangle();
+		state.isDrawPoint = context->isDrawPoint(true);
+		state.isDrawLine = context->isDrawLine(true);
+		state.isDrawTriangle = context->isDrawTriangle(true);
+		state.applySlopeDepthBias = context->isDrawTriangle(false) && (context->slopeDepthBias != 0.0f);
 		state.interpolateZ = context->depthBufferActive() || vPosZW;
 		state.interpolateW = context->pixelShader != nullptr;
 		state.frontFace = context->frontFace;
 		state.cullMode = context->cullMode;
-		state.slopeDepthBias = context->slopeDepthBias != 0.0f;
 
 		state.multiSample = context->sampleCount;
 		state.rasterizerDiscard = context->rasterizerDiscard;
diff --git a/src/Device/SetupProcessor.hpp b/src/Device/SetupProcessor.hpp
index 8a3374c..1c79710 100644
--- a/src/Device/SetupProcessor.hpp
+++ b/src/Device/SetupProcessor.hpp
@@ -42,11 +42,11 @@
 			bool isDrawPoint               : 1;
 			bool isDrawLine                : 1;
 			bool isDrawTriangle            : 1;
+			bool applySlopeDepthBias       : 1;
 			bool interpolateZ              : 1;
 			bool interpolateW              : 1;
 			VkFrontFace frontFace          : BITS(VK_FRONT_FACE_MAX_ENUM);
 			VkCullModeFlags cullMode       : BITS(VK_CULL_MODE_FLAG_BITS_MAX_ENUM);
-			bool slopeDepthBias            : 1;
 			unsigned int multiSample       : 3;   // 1, 2 or 4
 			bool rasterizerDiscard         : 1;
 
diff --git a/src/Pipeline/SetupRoutine.cpp b/src/Pipeline/SetupRoutine.cpp
index 80ac5df..7740cc1 100644
--- a/src/Pipeline/SetupRoutine.cpp
+++ b/src/Pipeline/SetupRoutine.cpp
@@ -427,7 +427,7 @@
 
 				Float c = z0;
 
-				if(state.isDrawTriangle && state.slopeDepthBias)
+				if(state.applySlopeDepthBias)
 				{
 					Float bias = Max(Abs(Float(A.x)), Abs(Float(B.x)));
 					bias *= *Pointer<Float>(data + OFFSET(DrawData,slopeDepthBias));
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index c86221c..4abb0f8 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -44,7 +44,7 @@
 		VK_TRUE,   // drawIndirectFirstInstance
 		VK_FALSE,  // depthClamp
 		VK_FALSE,  // depthBiasClamp
-		VK_FALSE,  // fillModeNonSolid
+		VK_TRUE,   // fillModeNonSolid
 		VK_FALSE,  // depthBounds
 		VK_FALSE,  // wideLines
 		VK_FALSE,  // largePoints
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index 89e835d..0e131f1 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -382,8 +382,7 @@
 
 	const VkPipelineRasterizationStateCreateInfo* rasterizationState = pCreateInfo->pRasterizationState;
 	if((rasterizationState->flags != 0) ||
-	   (rasterizationState->depthClampEnable != VK_FALSE) ||
-	   (rasterizationState->polygonMode != VK_POLYGON_MODE_FILL))
+	   (rasterizationState->depthClampEnable != VK_FALSE))
 	{
 		UNIMPLEMENTED("pCreateInfo->pRasterizationState settings");
 	}
@@ -391,6 +390,7 @@
 	context.rasterizerDiscard = (rasterizationState->rasterizerDiscardEnable == VK_TRUE);
 	context.cullMode = rasterizationState->cullMode;
 	context.frontFace = rasterizationState->frontFace;
+	context.polygonMode = rasterizationState->polygonMode;
 	context.depthBias = (rasterizationState->depthBiasEnable != VK_FALSE) ? rasterizationState->depthBiasConstantFactor : 0.0f;
 	context.slopeDepthBias = (rasterizationState->depthBiasEnable != VK_FALSE) ? rasterizationState->depthBiasSlopeFactor : 0.0f;