VK_KHR_shader_integer_dot_product implementation

This extension is entirely promoted to Vulkan 1.3. Because this is an
unoptimized implementation, all new properties are set to false.

The extension adds 3 integer dot products:
- signed
- unsigned
- mixed signed and unsigned

These dot products can be executed either on 32 bit integers or
packed (4 x 8 bit) vectors. Also, they may have the result of the
dot products added to an accumulator. This add operation is a
saturated add for both signed and unsigned dot product operations.

Tests: dEQP-VK.spirv_assembly.instruction.compute.op*khr.*
Bug: b/204502925
Change-Id: Id3c42563d4f3b073e98671215dc19fd9ca702e5c
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/63248
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
Commit-Queue: Alexis Hétu <sugoi@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index a4212c9..d6b5546 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -427,6 +427,10 @@
 				case spv::CapabilityStorageImageExtendedFormats: capabilities.StorageImageExtendedFormats = true; break;
 				case spv::CapabilityImageQuery: capabilities.ImageQuery = true; break;
 				case spv::CapabilityDerivativeControl: capabilities.DerivativeControl = true; break;
+				case spv::CapabilityDotProductInputAll: capabilities.DotProductInputAll = true; break;
+				case spv::CapabilityDotProductInput4x8Bit: capabilities.DotProductInput4x8Bit = true; break;
+				case spv::CapabilityDotProductInput4x8BitPacked: capabilities.DotProductInput4x8BitPacked = true; break;
+				case spv::CapabilityDotProduct: capabilities.DotProduct = true; break;
 				case spv::CapabilityInterpolationFunction: capabilities.InterpolationFunction = true; break;
 				case spv::CapabilityStorageImageWriteWithoutFormat: capabilities.StorageImageWriteWithoutFormat = true; break;
 				case spv::CapabilityGroupNonUniform: capabilities.GroupNonUniform = true; break;
@@ -651,6 +655,12 @@
 		case spv::OpIAddCarry:
 		case spv::OpISubBorrow:
 		case spv::OpDot:
+		case spv::OpSDot:
+		case spv::OpUDot:
+		case spv::OpSUDot:
+		case spv::OpSDotAccSat:
+		case spv::OpUDotAccSat:
+		case spv::OpSUDotAccSat:
 		case spv::OpConvertFToU:
 		case spv::OpConvertFToS:
 		case spv::OpConvertSToF:
@@ -787,6 +797,7 @@
 				if(!strcmp(ext, "SPV_KHR_multiview")) 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;
 				if(!strcmp(ext, "SPV_KHR_non_semantic_info")) break;
 				if(!strcmp(ext, "SPV_KHR_vulkan_memory_model")) break;
 				if(!strcmp(ext, "SPV_GOOGLE_decorate_string")) break;
@@ -2028,6 +2039,12 @@
 		return EmitBinaryOp(insn, state);
 
 	case spv::OpDot:
+	case spv::OpSDot:
+	case spv::OpUDot:
+	case spv::OpSUDot:
+	case spv::OpSDotAccSat:
+	case spv::OpUDotAccSat:
+	case spv::OpSUDotAccSat:
 		return EmitDot(insn, state);
 
 	case spv::OpSelect:
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index c3a9815..efcdde4 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -674,6 +674,10 @@
 		bool StorageImageExtendedFormats : 1;
 		bool ImageQuery : 1;
 		bool DerivativeControl : 1;
+		bool DotProductInputAll : 1;
+		bool DotProductInput4x8Bit : 1;
+		bool DotProductInput4x8BitPacked : 1;
+		bool DotProduct : 1;
 		bool InterpolationFunction : 1;
 		bool StorageImageWriteWithoutFormat : 1;
 		bool GroupNonUniform : 1;
@@ -1387,7 +1391,12 @@
 	static bool HasTypeAndResult(spv::Op op);
 
 	// Helper as we often need to take dot products as part of doing other things.
-	SIMD::Float Dot(unsigned numComponents, Operand const &x, Operand const &y) const;
+	static SIMD::Float FDot(unsigned numComponents, Operand const &x, Operand const &y);
+	static SIMD::Int SDot(unsigned numComponents, Operand const &x, Operand const &y, Operand const *accum);
+	static SIMD::UInt UDot(unsigned numComponents, Operand const &x, Operand const &y, Operand const *accum);
+	static SIMD::Int SUDot(unsigned numComponents, Operand const &x, Operand const &y, Operand const *accum);
+	static SIMD::Int AddSat(RValue<SIMD::Int> a, RValue<SIMD::Int> b);
+	static SIMD::UInt AddSat(RValue<SIMD::UInt> a, RValue<SIMD::UInt> b);
 
 	// Splits x into a floating-point significand in the range [0.5, 1.0)
 	// and an integral exponent of two, such that:
diff --git a/src/Pipeline/SpirvShaderArithmetic.cpp b/src/Pipeline/SpirvShaderArithmetic.cpp
index bc6bb59..0b56aa2 100644
--- a/src/Pipeline/SpirvShaderArithmetic.cpp
+++ b/src/Pipeline/SpirvShaderArithmetic.cpp
@@ -19,6 +19,8 @@
 
 #include <spirv/unified1/spirv.hpp>
 
+#include <limits>
+
 namespace sw {
 
 SpirvShader::EmitResult SpirvShader::EmitVectorTimesScalar(InsnIterator insn, EmitState *state) const
@@ -528,7 +530,43 @@
 	auto lhs = Operand(this, state, insn.word(3));
 	auto rhs = Operand(this, state, insn.word(4));
 
-	dst.move(0, Dot(lhsType.componentCount, lhs, rhs));
+	auto opcode = insn.opcode();
+	switch(opcode)
+	{
+	case spv::OpDot:
+		dst.move(0, FDot(lhsType.componentCount, lhs, rhs));
+		break;
+	case spv::OpSDot:
+		dst.move(0, SDot(lhsType.componentCount, lhs, rhs, nullptr));
+		break;
+	case spv::OpUDot:
+		dst.move(0, UDot(lhsType.componentCount, lhs, rhs, nullptr));
+		break;
+	case spv::OpSUDot:
+		dst.move(0, SUDot(lhsType.componentCount, lhs, rhs, nullptr));
+		break;
+	case spv::OpSDotAccSat:
+		{
+			auto accum = Operand(this, state, insn.word(5));
+			dst.move(0, SDot(lhsType.componentCount, lhs, rhs, &accum));
+		}
+		break;
+	case spv::OpUDotAccSat:
+		{
+			auto accum = Operand(this, state, insn.word(5));
+			dst.move(0, UDot(lhsType.componentCount, lhs, rhs, &accum));
+		}
+		break;
+	case spv::OpSUDotAccSat:
+		{
+			auto accum = Operand(this, state, insn.word(5));
+			dst.move(0, SUDot(lhsType.componentCount, lhs, rhs, &accum));
+		}
+		break;
+	default:
+		UNREACHABLE("%s", OpcodeName(opcode));
+		break;
+	}
 
 	SPIRV_SHADER_DBG("{0}: {1}", insn.resultId(), dst);
 	SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), lhs);
@@ -537,7 +575,7 @@
 	return EmitResult::Continue;
 }
 
-SIMD::Float SpirvShader::Dot(unsigned numComponents, Operand const &x, Operand const &y) const
+SIMD::Float SpirvShader::FDot(unsigned numComponents, Operand const &x, Operand const &y)
 {
 	SIMD::Float d = x.Float(0) * y.Float(0);
 
@@ -549,6 +587,140 @@
 	return d;
 }
 
+SIMD::Int SpirvShader::SDot(unsigned numComponents, Operand const &x, Operand const &y, Operand const *accum)
+{
+	SIMD::Int d(0);
+
+	if(numComponents == 1)  // 4x8bit packed
+	{
+		numComponents = 4;
+		for(auto i = 0u; i < numComponents; i++)
+		{
+			Int4 xs(As<SByte4>(Extract(x.Int(0), i)));
+			Int4 ys(As<SByte4>(Extract(y.Int(0), i)));
+
+			Int4 xy = xs * ys;
+			rr::Int sum = Extract(xy, 0) + Extract(xy, 1) + Extract(xy, 2) + Extract(xy, 3);
+
+			d = Insert(d, sum, i);
+		}
+	}
+	else
+	{
+		d = x.Int(0) * y.Int(0);
+
+		for(auto i = 1u; i < numComponents; i++)
+		{
+			d += x.Int(i) * y.Int(i);
+		}
+	}
+
+	if(accum)
+	{
+		d = AddSat(d, accum->Int(0));
+	}
+
+	return d;
+}
+
+SIMD::UInt SpirvShader::UDot(unsigned numComponents, Operand const &x, Operand const &y, Operand const *accum)
+{
+	SIMD::UInt d(0);
+
+	if(numComponents == 1)  // 4x8bit packed
+	{
+		numComponents = 4;
+		for(auto i = 0u; i < numComponents; i++)
+		{
+			Int4 xs(As<Byte4>(Extract(x.Int(0), i)));
+			Int4 ys(As<Byte4>(Extract(y.Int(0), i)));
+
+			UInt4 xy = xs * ys;
+			rr::UInt sum = Extract(xy, 0) + Extract(xy, 1) + Extract(xy, 2) + Extract(xy, 3);
+
+			d = Insert(d, sum, i);
+		}
+	}
+	else
+	{
+		d = x.UInt(0) * y.UInt(0);
+
+		for(auto i = 1u; i < numComponents; i++)
+		{
+			d += x.UInt(i) * y.UInt(i);
+		}
+	}
+
+	if(accum)
+	{
+		d = AddSat(d, accum->UInt(0));
+	}
+
+	return d;
+}
+
+SIMD::Int SpirvShader::SUDot(unsigned numComponents, Operand const &x, Operand const &y, Operand const *accum)
+{
+	SIMD::Int d(0);
+
+	if(numComponents == 1)  // 4x8bit packed
+	{
+		numComponents = 4;
+		for(auto i = 0u; i < numComponents; i++)
+		{
+			Int4 xs(As<SByte4>(Extract(x.Int(0), i)));
+			Int4 ys(As<Byte4>(Extract(y.Int(0), i)));
+
+			Int4 xy = xs * ys;
+			rr::Int sum = Extract(xy, 0) + Extract(xy, 1) + Extract(xy, 2) + Extract(xy, 3);
+
+			d = Insert(d, sum, i);
+		}
+	}
+	else
+	{
+		d = x.Int(0) * As<SIMD::Int>(y.UInt(0));
+
+		for(auto i = 1u; i < numComponents; i++)
+		{
+			d += x.Int(i) * As<SIMD::Int>(y.UInt(i));
+		}
+	}
+
+	if(accum)
+	{
+		d = AddSat(d, accum->Int(0));
+	}
+
+	return d;
+}
+
+SIMD::Int SpirvShader::AddSat(RValue<SIMD::Int> a, RValue<SIMD::Int> b)
+{
+	SIMD::Int sum = a + b;
+	SIMD::Int sSign = sum >> 31;
+	SIMD::Int aSign = a >> 31;
+	SIMD::Int bSign = b >> 31;
+
+	// Overflow happened if both numbers added have the same sign and the sum has a different sign
+	SIMD::Int oob = ~(aSign ^ bSign) & (aSign ^ sSign);
+	SIMD::Int overflow = oob & sSign;
+	SIMD::Int underflow = oob & aSign;
+
+	return (overflow & std::numeric_limits<int32_t>::max()) |
+	       (underflow & std::numeric_limits<int32_t>::min()) |
+	       (~oob & sum);
+}
+
+SIMD::UInt SpirvShader::AddSat(RValue<SIMD::UInt> a, RValue<SIMD::UInt> b)
+{
+	SIMD::UInt sum = a + b;
+
+	// Overflow happened if the sum of unsigned integers is smaller than either of the 2 numbers being added
+	// Note: CmpLT()'s return value is automatically set to UINT_MAX when true
+	return CmpLT(sum, a) | sum;
+}
+
 std::pair<SIMD::Float, SIMD::Int> SpirvShader::Frexp(RValue<SIMD::Float> val) const
 {
 	// Assumes IEEE 754
diff --git a/src/Pipeline/SpirvShaderGLSLstd450.cpp b/src/Pipeline/SpirvShaderGLSLstd450.cpp
index d1edb79..582a092 100644
--- a/src/Pipeline/SpirvShaderGLSLstd450.cpp
+++ b/src/Pipeline/SpirvShaderGLSLstd450.cpp
@@ -295,7 +295,7 @@
 			auto I = Operand(this, state, insn.word(5));
 			auto N = Operand(this, state, insn.word(6));
 
-			SIMD::Float d = Dot(type.componentCount, I, N);
+			SIMD::Float d = FDot(type.componentCount, I, N);
 
 			for(auto i = 0u; i < type.componentCount; i++)
 			{
@@ -310,7 +310,7 @@
 			auto eta = Operand(this, state, insn.word(7));
 			Decorations r = GetDecorationsForId(insn.resultId());
 
-			SIMD::Float d = Dot(type.componentCount, I, N);
+			SIMD::Float d = FDot(type.componentCount, I, N);
 			SIMD::Float k = SIMD::Float(1.0f) - eta.Float(0) * eta.Float(0) * (SIMD::Float(1.0f) - d * d);
 			SIMD::Int pos = CmpNLT(k, SIMD::Float(0.0f));
 			SIMD::Float t = (eta.Float(0) * d + Sqrt(k, r.RelaxedPrecision));
@@ -327,7 +327,7 @@
 			auto I = Operand(this, state, insn.word(6));
 			auto Nref = Operand(this, state, insn.word(7));
 
-			SIMD::Float d = Dot(type.componentCount, I, Nref);
+			SIMD::Float d = FDot(type.componentCount, I, Nref);
 			SIMD::Int neg = CmpLT(d, SIMD::Float(0.0f));
 
 			for(auto i = 0u; i < type.componentCount; i++)
@@ -340,7 +340,7 @@
 	case GLSLstd450Length:
 		{
 			auto x = Operand(this, state, insn.word(5));
-			SIMD::Float d = Dot(getObjectType(insn.word(5)).componentCount, x, x);
+			SIMD::Float d = FDot(getObjectType(insn.word(5)).componentCount, x, x);
 			Decorations r = GetDecorationsForId(insn.resultId());
 
 			dst.move(0, Sqrt(d, r.RelaxedPrecision));
@@ -351,7 +351,7 @@
 			auto x = Operand(this, state, insn.word(5));
 			Decorations r = GetDecorationsForId(insn.resultId());
 
-			SIMD::Float d = Dot(getObjectType(insn.word(5)).componentCount, x, x);
+			SIMD::Float d = FDot(getObjectType(insn.word(5)).componentCount, x, x);
 			SIMD::Float invLength = SIMD::Float(1.0f) / Sqrt(d, r.RelaxedPrecision);
 
 			for(auto i = 0u; i < type.componentCount; i++)
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index a6d59cc..01aae1b 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -388,6 +388,11 @@
 	features->synchronization2 = VK_TRUE;
 }
 
+static void getPhysicalDeviceShaderIntegerDotProductFeatures(VkPhysicalDeviceShaderIntegerDotProductFeatures *features)
+{
+	features->shaderIntegerDotProduct = VK_TRUE;
+}
+
 void PhysicalDevice::getFeatures2(VkPhysicalDeviceFeatures2 *features) const
 {
 	features->features = getFeatures();
@@ -509,6 +514,9 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES:
 			getPhysicalDeviceSynchronization2Features(reinterpret_cast<struct VkPhysicalDeviceSynchronization2Features *>(curExtension));
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES:
+			getPhysicalDeviceShaderIntegerDotProductFeatures(reinterpret_cast<struct VkPhysicalDeviceShaderIntegerDotProductFeatures *>(curExtension));
+			break;
 		// Unsupported extensions, but used by dEQP
 		// TODO(b/176893525): This may not be legal.
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT:
@@ -516,7 +524,6 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT:
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT:
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_2_FEATURES_EXT:
-		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES_KHR:
 			break;
 		default:
 			UNSUPPORTED("curExtension->sType: %s", vk::Stringify(curExtension->sType).c_str());
@@ -1151,6 +1158,40 @@
 	properties->uniformTexelBufferOffsetSingleTexelAlignment = VK_FALSE;
 }
 
+void PhysicalDevice::getProperties(VkPhysicalDeviceShaderIntegerDotProductProperties *properties) const
+{
+	properties->integerDotProduct8BitUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProduct8BitSignedAccelerated = VK_FALSE;
+	properties->integerDotProduct8BitMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProduct4x8BitPackedUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProduct4x8BitPackedSignedAccelerated = VK_FALSE;
+	properties->integerDotProduct4x8BitPackedMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProduct16BitUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProduct16BitSignedAccelerated = VK_FALSE;
+	properties->integerDotProduct16BitMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProduct32BitUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProduct32BitSignedAccelerated = VK_FALSE;
+	properties->integerDotProduct32BitMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProduct64BitUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProduct64BitSignedAccelerated = VK_FALSE;
+	properties->integerDotProduct64BitMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating8BitUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating8BitSignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating8BitMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating4x8BitPackedUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating4x8BitPackedSignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating4x8BitPackedMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating16BitUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating16BitSignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating16BitMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating32BitUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating32BitSignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating32BitMixedSignednessAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating64BitUnsignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating64BitSignedAccelerated = VK_FALSE;
+	properties->integerDotProductAccumulatingSaturating64BitMixedSignednessAccelerated = VK_FALSE;
+}
+
 template<typename T>
 static void getSamplerFilterMinmaxProperties(T *properties)
 {
@@ -1333,6 +1374,13 @@
 	       CheckFeature(requested, supported, descriptorBindingInlineUniformBlockUpdateAfterBind);
 }
 
+bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceShaderIntegerDotProductFeatures *requested) const
+{
+	auto supported = getSupportedFeatures(requested);
+
+	return CheckFeature(requested, supported, shaderIntegerDotProduct);
+}
+
 bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceExtendedDynamicStateFeaturesEXT *requested) const
 {
 	auto supported = getSupportedFeatures(requested);
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index f699fd9..ffbb5c7 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -48,6 +48,7 @@
 	bool hasExtendedFeatures(const VkPhysicalDeviceTextureCompressionASTCHDRFeatures *features) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceSubgroupSizeControlFeatures *requested) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceInlineUniformBlockFeatures *features) const;
+	bool hasExtendedFeatures(const VkPhysicalDeviceShaderIntegerDotProductFeatures *features) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures *requested) const;
 
 	const VkPhysicalDeviceProperties &getProperties() const;
@@ -83,6 +84,7 @@
 	void getProperties(VkPhysicalDeviceSubgroupSizeControlProperties *properties) const;
 	void getProperties(VkPhysicalDeviceInlineUniformBlockProperties *properties) const;
 	void getProperties(VkPhysicalDeviceTexelBufferAlignmentProperties *properties) const;
+	void getProperties(VkPhysicalDeviceShaderIntegerDotProductProperties *properties) const;
 	void getProperties(VkPhysicalDeviceVulkan11Properties *properties) const;
 
 	static void GetFormatProperties(Format format, VkFormatProperties *pFormatProperties);
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 31426e9..5c86cde 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -426,6 +426,7 @@
 	{ { VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, VK_KHR_DYNAMIC_RENDERING_SPEC_VERSION } },
 	{ { VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, VK_KHR_FORMAT_FEATURE_FLAGS_2_SPEC_VERSION } },
 	{ { VK_KHR_MAINTENANCE_4_EXTENSION_NAME, VK_KHR_MAINTENANCE_4_SPEC_VERSION } },
+	{ { VK_KHR_SHADER_INTEGER_DOT_PRODUCT_EXTENSION_NAME, VK_KHR_SHADER_INTEGER_DOT_PRODUCT_SPEC_VERSION } },
 	{ { VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, VK_KHR_SHADER_NON_SEMANTIC_INFO_SPEC_VERSION } },
 	{ { 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 } },
@@ -988,6 +989,16 @@
 				}
 			}
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_FEATURES:
+			{
+				const auto *integerDotProductFeatures = reinterpret_cast<const VkPhysicalDeviceShaderIntegerDotProductFeatures *>(extensionCreateInfo);
+				bool hasFeatures = vk::Cast(physicalDevice)->hasExtendedFeatures(integerDotProductFeatures);
+				if(!hasFeatures)
+				{
+					return VK_ERROR_FEATURE_NOT_PRESENT;
+				}
+			}
+			break;
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ZERO_INITIALIZE_WORKGROUP_MEMORY_FEATURES:
 			{
 				const auto *zeroInitializeWorkgroupMemoryFeatures = reinterpret_cast<const VkPhysicalDeviceZeroInitializeWorkgroupMemoryFeatures *>(extensionCreateInfo);
@@ -3582,6 +3593,12 @@
 				vk::Cast(physicalDevice)->getProperties(properties);
 			}
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_DOT_PRODUCT_PROPERTIES:
+			{
+				auto properties = reinterpret_cast<VkPhysicalDeviceShaderIntegerDotProductProperties *>(extensionProperties);
+				vk::Cast(physicalDevice)->getProperties(properties);
+			}
+			break;
 		default:
 			// "the [driver] must skip over, without processing (other than reading the sType and pNext members) any structures in the chain with sType values not defined by [supported extenions]"
 			UNSUPPORTED("pProperties->pNext sType = %s", vk::Stringify(extensionProperties->sType).c_str());