Implement VK_EXT_depth_clip_control

The extension allows for a depth range of [-1, 1], which aids in
layering OpenGL over Vulkan. After clipping, the depth is
renormalized to the standard Vulkan range of [0, 1].

Based on change by Sean Risser <srisser@google.com>.

Bug: b/207658159
Tests: dEQP-VK.pipeline.*.depth.*
Change-Id: I40297b39e3447296bf5f7c39386d47a0663aa792
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/67489
Tested-by: Shahbaz Youssefi <syoussefi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Presubmit-Ready: Shahbaz Youssefi <syoussefi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@google.com>
diff --git a/src/Device/Clipper.cpp b/src/Device/Clipper.cpp
index 54b27eb..e7f4caa 100644
--- a/src/Device/Clipper.cpp
+++ b/src/Device/Clipper.cpp
@@ -29,7 +29,7 @@
 	Vo.w = (dj * Vi.w - di * Vj.w) * D;
 }
 
-void clipNear(sw::Polygon &polygon)
+void clipNear(sw::Polygon &polygon, bool depthClipNegativeOneToOne)
 {
 	const sw::float4 **V = polygon.P[polygon.i];
 	const sw::float4 **T = polygon.P[polygon.i + 1];
@@ -43,6 +43,14 @@
 		float di = V[i]->z;
 		float dj = V[j]->z;
 
+		// When depthClipNegativeOneToOne is enabled, z is mapped to (z+w)/2.  Division by 2
+		// is not done, as distances are relative as far as clipEdge is concerned.
+		if(depthClipNegativeOneToOne)
+		{
+			di += V[i]->w;
+			dj += V[j]->w;
+		}
+
 		if(di >= 0)
 		{
 			T[t++] = V[i];
@@ -81,6 +89,13 @@
 		float di = V[i]->w - V[i]->z;
 		float dj = V[j]->w - V[j]->z;
 
+		// When depthClipNegativeOneToOne is enabled, z is mapped to (z+w)/2.  We have:
+		//
+		//     w - (z+w)/2 = (w - z)/2
+		//
+		// But division by 2 is not done, as distances are relative as far as clipEdge is
+		// concerned.  In short, no special handling is necessary.
+
 		if(di >= 0)
 		{
 			T[t++] = V[i];
@@ -265,7 +280,7 @@
 {
 	if(clipFlagsOr & CLIP_FRUSTUM)
 	{
-		if(clipFlagsOr & CLIP_NEAR) clipNear(polygon);
+		if(clipFlagsOr & CLIP_NEAR) clipNear(polygon, draw.depthClipNegativeOneToOne);
 		if(polygon.n >= 3)
 		{
 			if(clipFlagsOr & CLIP_FAR) clipFar(polygon);
diff --git a/src/Device/Context.cpp b/src/Device/Context.cpp
index 204ff55..84fc868 100644
--- a/src/Device/Context.cpp
+++ b/src/Device/Context.cpp
@@ -506,6 +506,27 @@
 		const VkPipelineDepthStencilStateCreateInfo *depthStencilState = pCreateInfo->pDepthStencilState;
 		const VkPipelineColorBlendStateCreateInfo *colorBlendState = pCreateInfo->pColorBlendState;
 
+		extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(viewportState->pNext);
+		while(extensionCreateInfo != nullptr)
+		{
+			switch(extensionCreateInfo->sType)
+			{
+			case VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT:
+				{
+					const auto *depthClipControl = reinterpret_cast<const VkPipelineViewportDepthClipControlCreateInfoEXT *>(extensionCreateInfo);
+					depthClipNegativeOneToOne = depthClipControl->negativeOneToOne != VK_FALSE;
+				}
+				break;
+			case VK_STRUCTURE_TYPE_MAX_ENUM:
+				// dEQP passes this value expecting the driver to ignore it.
+				break;
+			default:
+				UNSUPPORTED("pCreateInfo->pViewportState->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str());
+				break;
+			}
+			extensionCreateInfo = extensionCreateInfo->pNext;
+		}
+
 		if(viewportState->flags != 0)
 		{
 			// Vulkan 1.2: "flags is reserved for future use." "flags must be 0"
diff --git a/src/Device/Context.hpp b/src/Device/Context.hpp
index 0e5acd0..f3173b5 100644
--- a/src/Device/Context.hpp
+++ b/src/Device/Context.hpp
@@ -181,6 +181,7 @@
 	inline bool hasDepthRangeUnrestricted() const { return depthRangeUnrestricted; }
 	inline bool getDepthClampEnable() const { return depthClampEnable; }
 	inline bool getDepthClipEnable() const { return depthClipEnable; }
+	inline bool getDepthClipNegativeOneToOne() const { return depthClipNegativeOneToOne; }
 
 	// Pixel processor states
 	inline bool hasRasterizerDiscard() const { return rasterizerDiscard; }
@@ -286,6 +287,7 @@
 	bool depthWriteEnable = false;
 	bool depthClampEnable = false;
 	bool depthClipEnable = false;
+	bool depthClipNegativeOneToOne = false;
 
 	float lineWidth = 0.0f;
 
diff --git a/src/Device/Renderer.cpp b/src/Device/Renderer.cpp
index ce30ec5..2b6949a 100644
--- a/src/Device/Renderer.cpp
+++ b/src/Device/Renderer.cpp
@@ -267,6 +267,7 @@
 	draw->descriptorSetObjects = inputs.getDescriptorSetObjects();
 	draw->pipelineLayout = pipelineState.getPipelineLayout();
 	draw->depthClipEnable = pipelineState.getDepthClipEnable();
+	draw->depthClipNegativeOneToOne = pipelineState.getDepthClipNegativeOneToOne();
 
 	draw->vertexRoutine = vertexRoutine;
 	draw->setupRoutine = setupRoutine;
@@ -356,6 +357,13 @@
 		data->slopeDepthBias = pipelineState.getSlopeDepthBias();
 		data->depthBiasClamp = pipelineState.getDepthBiasClamp();
 
+		// Adjust viewport transform based on the negativeOneToOne state.
+		if(pipelineState.getDepthClipNegativeOneToOne())
+		{
+			data->depthRange = Z * 0.5f;
+			data->depthNear = (F + N) * 0.5f;
+		}
+
 		const vk::Attachments attachments = pipeline->getAttachments();
 		if(attachments.depthBuffer)
 		{
diff --git a/src/Device/Renderer.hpp b/src/Device/Renderer.hpp
index 8263522..2f15e9f 100644
--- a/src/Device/Renderer.hpp
+++ b/src/Device/Renderer.hpp
@@ -153,6 +153,7 @@
 	VkLineRasterizationModeEXT lineRasterizationMode;
 
 	bool depthClipEnable;
+	bool depthClipNegativeOneToOne;
 
 	VertexProcessor::RoutineType vertexRoutine;
 	SetupProcessor::RoutineType setupRoutine;
diff --git a/src/Device/VertexProcessor.cpp b/src/Device/VertexProcessor.cpp
index 833fa6a..42df2d2 100644
--- a/src/Device/VertexProcessor.cpp
+++ b/src/Device/VertexProcessor.cpp
@@ -74,6 +74,7 @@
 	state.robustBufferAccess = pipelineState.getRobustBufferAccess();
 	state.isPoint = pipelineState.getTopology() == VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
 	state.depthClipEnable = pipelineState.getDepthClipEnable();
+	state.depthClipNegativeOneToOne = pipelineState.getDepthClipNegativeOneToOne();
 
 	for(size_t i = 0; i < MAX_INTERFACE_COMPONENTS / 4; i++)
 	{
diff --git a/src/Device/VertexProcessor.hpp b/src/Device/VertexProcessor.hpp
index e8f6b14..f4f7e3a 100644
--- a/src/Device/VertexProcessor.hpp
+++ b/src/Device/VertexProcessor.hpp
@@ -82,6 +82,7 @@
 		bool robustBufferAccess : 1;
 		bool isPoint : 1;
 		bool depthClipEnable : 1;
+		bool depthClipNegativeOneToOne : 1;
 	};
 
 	struct State : States
diff --git a/src/Pipeline/VertexRoutine.cpp b/src/Pipeline/VertexRoutine.cpp
index 36ce92c..7877eee 100644
--- a/src/Pipeline/VertexRoutine.cpp
+++ b/src/Pipeline/VertexRoutine.cpp
@@ -136,8 +136,9 @@
 		clipFlags |= minY & Clipper::CLIP_BOTTOM;
 		if(state.depthClipEnable)
 		{
+			// If depthClipNegativeOneToOne is enabled, depth values are in [-1, 1] instead of [0, 1].
 			SIMD::Int maxZ = CmpLT(posW, posZ);
-			SIMD::Int minZ = CmpNLE(0.0f, posZ);
+			SIMD::Int minZ = CmpNLE(state.depthClipNegativeOneToOne ? -posW : 0.0f, posZ);
 			clipFlags |= maxZ & Clipper::CLIP_FAR;
 			clipFlags |= minZ & Clipper::CLIP_NEAR;
 		}
@@ -148,7 +149,7 @@
 		SIMD::Int finiteZ = CmpLE(Abs(posZ), maxPos);
 
 		SIMD::Int finiteXYZ = finiteX & finiteY & finiteZ;
-		clipFlags |= finiteXYZ & 0x00000080;
+		clipFlags |= finiteXYZ & Clipper::CLIP_FINITE;
 	}
 }
 
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 1010d65..601bc23 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -454,6 +454,11 @@
 	features->rasterizationOrderStencilAttachmentAccess = VK_TRUE;
 }
 
+static void getPhysicalDeviceDepthClipControlFeaturesExt(VkPhysicalDeviceDepthClipControlFeaturesEXT *features)
+{
+	features->depthClipControl = VK_TRUE;
+}
+
 void PhysicalDevice::getFeatures2(VkPhysicalDeviceFeatures2 *features) const
 {
 	features->features = getFeatures();
@@ -603,6 +608,9 @@
 			// Workaround for a test bug (see https://gitlab.khronos.org/Tracker/vk-gl-cts/-/issues/3564)
 			reinterpret_cast<struct VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT *>(curExtension)->texelBufferAlignment = VK_TRUE;
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT:
+			getPhysicalDeviceDepthClipControlFeaturesExt(reinterpret_cast<struct VkPhysicalDeviceDepthClipControlFeaturesEXT *>(curExtension));
+			break;
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBPASS_MERGE_FEEDBACK_FEATURES_EXT:
 			// TODO(b/216982034): Workaround for a test bug (see https://gitlab.khronos.org/Tracker/vk-gl-cts/-/issues/3879)
 			reinterpret_cast<struct VkPhysicalDeviceSubpassMergeFeedbackFeaturesEXT *>(curExtension)->subpassMergeFeedback = VK_FALSE;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 0476145..be35d3a 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -402,6 +402,7 @@
 	{ { VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, VK_KHR_SYNCHRONIZATION_2_SPEC_VERSION } },
 	{ { VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_EXTENSION_NAME, VK_KHR_ZERO_INITIALIZE_WORKGROUP_MEMORY_SPEC_VERSION } },
 	// Additional extension
+	{ { VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_CONTROL_SPEC_VERSION } },
 	{ { VK_GOOGLE_DECORATE_STRING_EXTENSION_NAME, VK_GOOGLE_DECORATE_STRING_SPEC_VERSION } },
 	{ { VK_GOOGLE_HLSL_FUNCTIONALITY_1_EXTENSION_NAME, VK_GOOGLE_HLSL_FUNCTIONALITY_1_SPEC_VERSION } },
 	{ { VK_GOOGLE_USER_TYPE_EXTENSION_NAME, VK_GOOGLE_USER_TYPE_SPEC_VERSION } },
@@ -1060,6 +1061,7 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES:
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES:
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT:
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT:
 			break;
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT:
 			// Workaround for a test bug (see https://gitlab.khronos.org/Tracker/vk-gl-cts/-/issues/3564)