VK_EXT_shader_demote_to_helper_invocation support

This extension adds Vulkan support for the
SPV_EXT_demote_to_helper_invocation SPIR-V extension. That
SPIR-V extension provides a new instruction
OpDemoteToHelperInvocationEXT allowing shaders to “demote”
a fragment shader invocation to behave like a helper
invocation for its duration. The demoted invocation will
have no further side effects and will not output to the
framebuffer, but remains active and can participate in
computing derivatives and in group operations. This is a
better match for the “discard” instruction in HLSL.

Tests: dEQP-VK.rasterization.frag_side_effects.*.demote
Tests: dEQP-VK.glsl.demote.*
Tests: dEQP-VK.glsl.discard.*
Bug: b/204502919
Change-Id: I2a1d5ab2cda5a4f57ef9798861e3ea915c39c20c
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/64408
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index d6b5546..24bc4c6 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -257,6 +257,10 @@
 			}
 			break;
 
+		case spv::OpDemoteToHelperInvocation:
+			analysis.ContainsKill = true;
+			break;
+
 		case spv::OpLoopMerge:
 		case spv::OpSelectionMerge:
 			break;  // Nothing to do in analysis pass.
@@ -441,6 +445,7 @@
 				case spv::CapabilityGroupNonUniformShuffleRelative: capabilities.GroupNonUniformShuffleRelative = true; break;
 				case spv::CapabilityDeviceGroup: capabilities.DeviceGroup = true; break;
 				case spv::CapabilityMultiView: capabilities.MultiView = true; break;
+				case spv::CapabilityDemoteToHelperInvocation: capabilities.DemoteToHelperInvocation = true; break;
 				case spv::CapabilityStencilExportEXT: capabilities.StencilExportEXT = true; break;
 				case spv::CapabilityVulkanMemoryModel: capabilities.VulkanMemoryModel = true; break;
 				case spv::CapabilityVulkanMemoryModelDeviceScope: capabilities.VulkanMemoryModelDeviceScope = true; break;
@@ -748,6 +753,7 @@
 		case spv::OpCopyObject:
 		case spv::OpCopyLogical:
 		case spv::OpArrayLength:
+		case spv::OpIsHelperInvocationEXT:
 			// Instructions that yield an intermediate value or divergent pointer
 			DefineResult(insn);
 			break;
@@ -795,6 +801,7 @@
 				if(!strcmp(ext, "SPV_KHR_variable_pointers")) break;
 				if(!strcmp(ext, "SPV_KHR_device_group")) break;
 				if(!strcmp(ext, "SPV_KHR_multiview")) break;
+				if(!strcmp(ext, "SPV_EXT_demote_to_helper_invocation")) break;
 				if(!strcmp(ext, "SPV_EXT_shader_stencil_export")) break;
 				if(!strcmp(ext, "SPV_KHR_float_controls")) break;
 				if(!strcmp(ext, "SPV_KHR_integer_dot_product")) break;
@@ -2087,6 +2094,12 @@
 	case spv::OpKill:
 		return EmitKill(insn, state);
 
+	case spv::OpDemoteToHelperInvocation:
+		return EmitDemoteToHelperInvocation(insn, state);
+
+	case spv::OpIsHelperInvocationEXT:
+		return EmitIsHelperInvocation(insn, state);
+
 	case spv::OpImageSampleImplicitLod:
 	case spv::OpImageSampleExplicitLod:
 	case spv::OpImageSampleDrefImplicitLod:
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index efcdde4..65ebf69 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -688,6 +688,7 @@
 		bool GroupNonUniformArithmetic : 1;
 		bool DeviceGroup : 1;
 		bool MultiView : 1;
+		bool DemoteToHelperInvocation : 1;
 		bool StencilExportEXT : 1;
 		bool VulkanMemoryModel : 1;
 		bool VulkanMemoryModelDeviceScope : 1;
@@ -1265,6 +1266,7 @@
 
 	// Updates the current active lane mask.
 	void SetActiveLaneMask(RValue<SIMD::Int> mask, EmitState *state) const;
+	void SetStoresAndAtomicsMask(RValue<SIMD::Int> mask, EmitState *state) const;
 
 	// Emit all the unvisited blocks (except for ignore) in DFS order,
 	// starting with id.
@@ -1308,6 +1310,8 @@
 	EmitResult EmitUnreachable(InsnIterator insn, EmitState *state) const;
 	EmitResult EmitReturn(InsnIterator insn, EmitState *state) const;
 	EmitResult EmitKill(InsnIterator insn, EmitState *state) const;
+	EmitResult EmitDemoteToHelperInvocation(InsnIterator insn, EmitState *state) const;
+	EmitResult EmitIsHelperInvocation(InsnIterator insn, EmitState *state) const;
 	EmitResult EmitFunctionCall(InsnIterator insn, EmitState *state) const;
 	EmitResult EmitPhi(InsnIterator insn, EmitState *state) const;
 	EmitResult EmitImageSample(const ImageInstruction &instruction, EmitState *state) const;
diff --git a/src/Pipeline/SpirvShaderControlFlow.cpp b/src/Pipeline/SpirvShaderControlFlow.cpp
index 2b55ca4..bdd1923 100644
--- a/src/Pipeline/SpirvShaderControlFlow.cpp
+++ b/src/Pipeline/SpirvShaderControlFlow.cpp
@@ -579,6 +579,22 @@
 	return EmitResult::Terminator;
 }
 
+SpirvShader::EmitResult SpirvShader::EmitDemoteToHelperInvocation(InsnIterator insn, EmitState *state) const
+{
+	state->routine->helperInvocation |= state->activeLaneMask();
+	state->routine->killMask |= SignMask(state->activeLaneMask());
+	SetStoresAndAtomicsMask(state->storesAndAtomicsMask() & ~state->activeLaneMask(), state);
+	return EmitResult::Continue;
+}
+
+SpirvShader::EmitResult SpirvShader::EmitIsHelperInvocation(InsnIterator insn, EmitState *state) const
+{
+	auto &type = getType(insn.resultTypeId());
+	auto &dst = state->createIntermediate(insn.resultId(), type.componentCount);
+	dst.move(0, state->routine->helperInvocation);
+	return EmitResult::Continue;
+}
+
 SpirvShader::EmitResult SpirvShader::EmitFunctionCall(InsnIterator insn, EmitState *state) const
 {
 	auto functionId = Function::ID(insn.word(3));
@@ -724,6 +740,11 @@
 	dbgUpdateActiveLaneMask(mask, state);
 }
 
+void SpirvShader::SetStoresAndAtomicsMask(RValue<SIMD::Int> mask, EmitState *state) const
+{
+	state->storesAndAtomicsMaskValue = mask.value();
+}
+
 void SpirvShader::WriteCFGGraphVizDotFile(const char *path) const
 {
 	std::ofstream file(path);
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 22a99de..a184c1c 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -322,7 +322,7 @@
 template<typename T>
 static void getPhysicalDeviceShaderDemoteToHelperInvocationFeatures(T *features)
 {
-	features->shaderDemoteToHelperInvocation = VK_FALSE;
+	features->shaderDemoteToHelperInvocation = VK_TRUE;
 }
 
 template<typename T>
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 0908ba8..98e299c 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -421,6 +421,7 @@
 	{ { VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME, VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_SPEC_VERSION } },
 	{ { VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME, VK_EXT_PIPELINE_CREATION_FEEDBACK_SPEC_VERSION } },
 	{ { VK_EXT_PRIVATE_DATA_EXTENSION_NAME, VK_EXT_PRIVATE_DATA_SPEC_VERSION } },
+	{ { VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_SPEC_VERSION } },
 	{ { VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, VK_EXT_SUBGROUP_SIZE_CONTROL_SPEC_VERSION } },
 	{ { VK_EXT_TOOLING_INFO_EXTENSION_NAME, VK_EXT_TOOLING_INFO_SPEC_VERSION } },
 	{ { VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME, VK_KHR_COPY_COMMANDS_2_SPEC_VERSION } },