Implement VK_EXT_custom_border_color

This implementation uses the custom border color as "baked" state,
instead of data stored in the descriptor. This is justified by the
assumption that only few custom border colors will be used in practice.
maxSamplerAllocationCount and maxCustomBorderColorSamplers are both
set to 4000, like many other implementations currently:
https://vulkan.gpuinfo.org/displayextensionproperty.php?name=maxCustomBorderColorSamplers

Note that infinity is deemed a valid border component value, and Reactor
intentionally asserts when trying to initialize a Float constant with
infinity, so we bit-cast to int in C++.

Bug: b/151215666
Bug: angleproject:6200
Tests: dEQP-VK.*clamp_to_border_custom*
Change-Id: I22608a80f50194bbac66cf515b0a9249b67b5f00
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/54469
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Sean Risser <srisser@google.com>
Commit-Queue: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Device/Sampler.hpp b/src/Device/Sampler.hpp
index e42fb30..06c59ef 100644
--- a/src/Device/Sampler.hpp
+++ b/src/Device/Sampler.hpp
@@ -103,6 +103,7 @@
 	bool compareEnable;
 	VkCompareOp compareOp;
 	VkBorderColor border;
+	VkClearColorValue customBorder;
 	bool unnormalizedCoordinates;
 
 	VkSamplerYcbcrModelConversion ycbcrModel;
diff --git a/src/Pipeline/SamplerCore.cpp b/src/Pipeline/SamplerCore.cpp
index 655ec3f..d821e46 100644
--- a/src/Pipeline/SamplerCore.cpp
+++ b/src/Pipeline/SamplerCore.cpp
@@ -2120,45 +2120,69 @@
 
 Vector4f SamplerCore::replaceBorderTexel(const Vector4f &c, Int4 valid)
 {
-	Int4 borderRGB;
-	Int4 borderA;
+	Vector4i border;
 
 	bool scaled = !hasFloatTexture() && !hasUnnormalizedIntegerTexture() && !state.compareEnable;
 	bool sign = !hasUnsignedTextureComponent(0);
-	Int4 float_one = scaled ? As<Int4>(Float4(static_cast<float>(sign ? 0x7FFF : 0xFFFF))) : As<Int4>(Float4(1.0f));
+	float scale = scaled ? static_cast<float>(sign ? 0x7FFF : 0xFFFF) : 1.0f;
+	Int4 float_one = As<Int4>(Float4(scale));
 
 	switch(state.border)
 	{
 	case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
 	case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:
-		borderRGB = Int4(0);
-		borderA = Int4(0);
+		border.x = Int4(0);
+		border.y = Int4(0);
+		border.z = Int4(0);
+		border.w = Int4(0);
 		break;
 	case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
-		borderRGB = Int4(0);
-		borderA = float_one;
+		border.x = Int4(0);
+		border.y = Int4(0);
+		border.z = Int4(0);
+		border.w = float_one;
 		break;
 	case VK_BORDER_COLOR_INT_OPAQUE_BLACK:
-		borderRGB = Int4(0);
-		borderA = Int4(1);
+		border.x = Int4(0);
+		border.y = Int4(0);
+		border.z = Int4(0);
+		border.w = Int4(1);
 		break;
 	case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
-		borderRGB = float_one;
-		borderA = float_one;
+		border.x = float_one;
+		border.y = float_one;
+		border.z = float_one;
+		border.w = float_one;
 		break;
 	case VK_BORDER_COLOR_INT_OPAQUE_WHITE:
-		borderRGB = Int4(1);
-		borderA = Int4(1);
+		border.x = Int4(1);
+		border.y = Int4(1);
+		border.z = Int4(1);
+		border.w = Int4(1);
+		break;
+	case VK_BORDER_COLOR_FLOAT_CUSTOM_EXT:
+		// This bit-casts from float to int in C++ code instead of Reactor code
+		// because Reactor does not guarantee preserving infinity (b/140302841).
+		border.x = Int4(bit_cast<int>(scale * state.customBorder.float32[0]));
+		border.y = Int4(bit_cast<int>(scale * state.customBorder.float32[1]));
+		border.z = Int4(bit_cast<int>(scale * state.customBorder.float32[2]));
+		border.w = Int4(bit_cast<int>(scale * state.customBorder.float32[3]));
+		break;
+	case VK_BORDER_COLOR_INT_CUSTOM_EXT:
+		border.x = Int4(state.customBorder.int32[0]);
+		border.y = Int4(state.customBorder.int32[1]);
+		border.z = Int4(state.customBorder.int32[2]);
+		border.w = Int4(state.customBorder.int32[3]);
 		break;
 	default:
 		UNSUPPORTED("sint/uint/sfloat border: %u", state.border);
 	}
 
 	Vector4f out;
-	out.x = As<Float4>((valid & As<Int4>(c.x)) | (~valid & borderRGB));  // TODO: IfThenElse()
-	out.y = As<Float4>((valid & As<Int4>(c.y)) | (~valid & borderRGB));
-	out.z = As<Float4>((valid & As<Int4>(c.z)) | (~valid & borderRGB));
-	out.w = As<Float4>((valid & As<Int4>(c.w)) | (~valid & borderA));
+	out.x = As<Float4>((valid & As<Int4>(c.x)) | (~valid & border.x));  // TODO: IfThenElse()
+	out.y = As<Float4>((valid & As<Int4>(c.y)) | (~valid & border.y));
+	out.z = As<Float4>((valid & As<Int4>(c.z)) | (~valid & border.z));
+	out.w = As<Float4>((valid & As<Int4>(c.w)) | (~valid & border.w));
 
 	return out;
 }
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index fd6dc70..97aaf8f 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -62,6 +62,7 @@
 		{
 			samplerState.textureFilter = convertFilterMode(vkSamplerState, type, samplerMethod);
 			samplerState.border = vkSamplerState->borderColor;
+			samplerState.customBorder = vkSamplerState->customBorderColor;
 
 			samplerState.mipmapFilter = convertMipmapMode(vkSamplerState);
 			samplerState.highPrecisionFiltering = (vkSamplerState->filteringPrecision == VK_SAMPLER_FILTERING_PRECISION_MODE_HIGH_GOOGLE);
diff --git a/src/Vulkan/VkConfig.hpp b/src/Vulkan/VkConfig.hpp
index ee850e3..9317549 100644
--- a/src/Vulkan/VkConfig.hpp
+++ b/src/Vulkan/VkConfig.hpp
@@ -80,6 +80,8 @@
 	MAX_POINT_SIZE = 1023,
 };
 
+constexpr int MAX_SAMPLER_ALLOCATION_COUNT = 4000;
+
 constexpr int SUBPIXEL_PRECISION_BITS = 4;
 constexpr float SUBPIXEL_PRECISION_FACTOR = static_cast<float>(1 << SUBPIXEL_PRECISION_BITS);
 constexpr int SUBPIXEL_PRECISION_MASK = 0xFFFFFFFF >> (32 - SUBPIXEL_PRECISION_BITS);
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 8bf818b..af404a4 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -325,6 +325,12 @@
 	features->depthClipEnable = VK_TRUE;
 }
 
+static void getPhysicalDevicCustomBorderColorFeaturesExt(VkPhysicalDeviceCustomBorderColorFeaturesEXT *features)
+{
+	features->customBorderColors = VK_TRUE;
+	features->customBorderColorWithoutFormat = VK_TRUE;
+}
+
 void PhysicalDevice::getFeatures2(VkPhysicalDeviceFeatures2 *features) const
 {
 	features->features = getFeatures();
@@ -414,6 +420,9 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT:
 			getPhysicalDeviceDepthClipEnableFeaturesExt(reinterpret_cast<VkPhysicalDeviceDepthClipEnableFeaturesEXT *>(curExtension));
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT:
+			getPhysicalDevicCustomBorderColorFeaturesExt(reinterpret_cast<VkPhysicalDeviceCustomBorderColorFeaturesEXT *>(curExtension));
+			break;
 		default:
 			LOG_TRAP("curExtension->pNext->sType = %s", vk::Stringify(curExtension->sType).c_str());
 			break;
@@ -442,7 +451,7 @@
 		(1ul << 27),                                      // maxStorageBufferRange
 		vk::MAX_PUSH_CONSTANT_SIZE,                       // maxPushConstantsSize
 		4096,                                             // maxMemoryAllocationCount
-		4000,                                             // maxSamplerAllocationCount
+		vk::MAX_SAMPLER_ALLOCATION_COUNT,                 // maxSamplerAllocationCount
 		131072,                                           // bufferImageGranularity
 		0,                                                // sparseAddressSpaceSize (unsupported)
 		MAX_BOUND_DESCRIPTOR_SETS,                        // maxBoundDescriptorSets
@@ -993,6 +1002,11 @@
 	getDepthStencilResolveProperties(properties);
 }
 
+void PhysicalDevice::getProperties(VkPhysicalDeviceCustomBorderColorPropertiesEXT *properties) const
+{
+	properties->maxCustomBorderColorSamplers = MAX_SAMPLER_ALLOCATION_COUNT;
+}
+
 template<typename T>
 static void getSamplerFilterMinmaxProperties(T *properties)
 {
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index ba400af..b79a94a 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -65,6 +65,7 @@
 	void getProperties(VkPhysicalDeviceVulkan12Properties *properties) const;
 	void getProperties(VkPhysicalDeviceDescriptorIndexingProperties *properties) const;
 	void getProperties(VkPhysicalDeviceDepthStencilResolveProperties *properties) const;
+	void getProperties(VkPhysicalDeviceCustomBorderColorPropertiesEXT *properties) const;
 	void getProperties(VkPhysicalDeviceVulkan11Properties *properties) const;
 
 	static void GetFormatProperties(Format format, VkFormatProperties *pFormatProperties);
diff --git a/src/Vulkan/VkSampler.cpp b/src/Vulkan/VkSampler.cpp
index d64a4d5..1458fa3 100644
--- a/src/Vulkan/VkSampler.cpp
+++ b/src/Vulkan/VkSampler.cpp
@@ -16,7 +16,8 @@
 
 namespace vk {
 
-SamplerState::SamplerState(const VkSamplerCreateInfo *pCreateInfo, const vk::SamplerYcbcrConversion *ycbcrConversion, VkSamplerFilteringPrecisionModeGOOGLE filteringPrecision)
+SamplerState::SamplerState(const VkSamplerCreateInfo *pCreateInfo, const vk::SamplerYcbcrConversion *ycbcrConversion,
+                           VkSamplerFilteringPrecisionModeGOOGLE filteringPrecision, const VkClearColorValue &customBorderColor)
     : Memset(this, 0)
     , magFilter(pCreateInfo->magFilter)
     , minFilter(pCreateInfo->minFilter)
@@ -32,6 +33,7 @@
     , minLod(ClampLod(pCreateInfo->minLod))
     , maxLod(ClampLod(pCreateInfo->maxLod))
     , borderColor(pCreateInfo->borderColor)
+    , customBorderColor(customBorderColor)
     , unnormalizedCoordinates(pCreateInfo->unnormalizedCoordinates)
     , filteringPrecision(filteringPrecision)
 {
diff --git a/src/Vulkan/VkSampler.hpp b/src/Vulkan/VkSampler.hpp
index f713ed2..8684188 100644
--- a/src/Vulkan/VkSampler.hpp
+++ b/src/Vulkan/VkSampler.hpp
@@ -26,7 +26,8 @@
 
 struct SamplerState : sw::Memset<SamplerState>
 {
-	SamplerState(const VkSamplerCreateInfo *pCreateInfo, const vk::SamplerYcbcrConversion *ycbcrConversion, VkSamplerFilteringPrecisionModeGOOGLE filteringPrecision);
+	SamplerState(const VkSamplerCreateInfo *pCreateInfo, const vk::SamplerYcbcrConversion *ycbcrConversion,
+	             VkSamplerFilteringPrecisionModeGOOGLE filteringPrecision, const VkClearColorValue &customBorderColor);
 
 	// Prevents accessing mipmap levels out of range.
 	static float ClampLod(float lod)
@@ -48,6 +49,7 @@
 	const float minLod = 0.0f;
 	const float maxLod = 0.0f;
 	const VkBorderColor borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
+	const VkClearColorValue customBorderColor = {};
 	const VkBool32 unnormalizedCoordinates = VK_FALSE;
 
 	VkSamplerYcbcrModelConversion ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index a410e7a..92dcf97 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -366,6 +366,7 @@
 	{ { VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION } },
 	{ { VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_EXTENSION_NAME, VK_KHR_SEPARATE_DEPTH_STENCIL_LAYOUTS_SPEC_VERSION } },
 	{ { VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_ENABLE_SPEC_VERSION } },
+	{ { VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, VK_EXT_CUSTOM_BORDER_COLOR_SPEC_VERSION } },
 	// Only 1.1 core version of this is supported. The extension has additional requirements
 	//{{ VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME, VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION }},
 	{ { VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_SPEC_VERSION } },
@@ -877,6 +878,15 @@
 				(void)tsFeatures->timelineSemaphore;
 			}
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT:
+			{
+				const auto *customBorderColorFeatures = reinterpret_cast<const VkPhysicalDeviceCustomBorderColorFeaturesEXT *>(extensionCreateInfo);
+
+				// VK_EXT_custom_border_color is always enabled
+				(void)customBorderColorFeatures->customBorderColors;
+				(void)customBorderColorFeatures->customBorderColorWithoutFormat;
+			}
+			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]"
 			LOG_TRAP("pCreateInfo->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str());
@@ -2119,6 +2129,7 @@
 	const VkBaseInStructure *extensionCreateInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
 	const vk::SamplerYcbcrConversion *ycbcrConversion = nullptr;
 	VkSamplerFilteringPrecisionModeGOOGLE filteringPrecision = VK_SAMPLER_FILTERING_PRECISION_MODE_LOW_GOOGLE;
+	VkClearColorValue borderColor = {};
 
 	while(extensionCreateInfo)
 	{
@@ -2126,7 +2137,8 @@
 		{
 		case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO:
 			{
-				const VkSamplerYcbcrConversionInfo *samplerYcbcrConversionInfo = reinterpret_cast<const VkSamplerYcbcrConversionInfo *>(extensionCreateInfo);
+				const VkSamplerYcbcrConversionInfo *samplerYcbcrConversionInfo =
+				    reinterpret_cast<const VkSamplerYcbcrConversionInfo *>(extensionCreateInfo);
 				ycbcrConversion = vk::Cast(samplerYcbcrConversionInfo->conversion);
 			}
 			break;
@@ -2139,6 +2151,14 @@
 			}
 			break;
 #endif
+		case VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT:
+			{
+				const VkSamplerCustomBorderColorCreateInfoEXT *borderColorInfo =
+				    reinterpret_cast<const VkSamplerCustomBorderColorCreateInfoEXT *>(extensionCreateInfo);
+
+				borderColor = borderColorInfo->customBorderColor;
+			}
+			break;
 		default:
 			LOG_TRAP("pCreateInfo->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str());
 			break;
@@ -2147,7 +2167,7 @@
 		extensionCreateInfo = extensionCreateInfo->pNext;
 	}
 
-	vk::SamplerState samplerState(pCreateInfo, ycbcrConversion, filteringPrecision);
+	vk::SamplerState samplerState(pCreateInfo, ycbcrConversion, filteringPrecision, borderColor);
 	uint32_t samplerID = vk::Cast(device)->indexSampler(samplerState);
 
 	VkResult result = vk::Sampler::Create(pAllocator, pCreateInfo, pSampler, samplerState, samplerID);
@@ -3192,6 +3212,12 @@
 				vk::Cast(physicalDevice)->getProperties(properties);
 			}
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT:
+			{
+				auto properties = reinterpret_cast<VkPhysicalDeviceCustomBorderColorPropertiesEXT *>(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]"
 			LOG_TRAP("pProperties->pNext sType = %s", vk::Stringify(extensionProperties->sType).c_str());