1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2021 The Khronos Group Inc.
6 * Copyright (c) 2021 Valve Corporation.
7 * Copyright (c) 2023 LunarG, Inc.
8 * Copyright (c) 2023 Nintendo
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 *      http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 *
22 *//*!
23 * \file
24 * \brief Border color swizzle tests
25 *//*--------------------------------------------------------------------*/
26#include "vktPipelineSamplerBorderSwizzleTests.hpp"
27#include "vktPipelineImageUtil.hpp"
28
29#include "vkImageUtil.hpp"
30#include "vkQueryUtil.hpp"
31#include "vkImageWithMemory.hpp"
32#include "vkTypeUtil.hpp"
33#include "vkObjUtil.hpp"
34#include "vkBuilderUtil.hpp"
35#include "vkCmdUtil.hpp"
36#include "vkBarrierUtil.hpp"
37
38#include "tcuMaybe.hpp"
39#include "tcuTextureUtil.hpp"
40#include "tcuVectorUtil.hpp"
41#include "tcuFloat.hpp"
42
43#include "deRandom.hpp"
44
45#include <string>
46#include <sstream>
47#include <array>
48#include <cstring>
49#include <algorithm>
50
51namespace vkt
52{
53namespace pipeline
54{
55
56namespace
57{
58
59using namespace vk;
60
61// Returns true if the mapping doesn't alter each component.
62bool isIdentitySwizzle (const VkComponentMapping& mapping)
63{
64	return (
65		(mapping.r == VK_COMPONENT_SWIZZLE_R || mapping.r == VK_COMPONENT_SWIZZLE_IDENTITY) &&
66		(mapping.g == VK_COMPONENT_SWIZZLE_G || mapping.g == VK_COMPONENT_SWIZZLE_IDENTITY) &&
67		(mapping.b == VK_COMPONENT_SWIZZLE_B || mapping.b == VK_COMPONENT_SWIZZLE_IDENTITY) &&
68		(mapping.a == VK_COMPONENT_SWIZZLE_A || mapping.a == VK_COMPONENT_SWIZZLE_IDENTITY)
69	);
70}
71
72struct TestParams
73{
74	PipelineConstructionType		pipelineConstructionType;
75	VkFormat						textureFormat;
76	VkClearColorValue				textureColor;
77	VkClearDepthStencilValue		textureDepthStencilValue;
78	VkComponentMapping				componentMapping;
79	VkBorderColor					borderColor;
80	tcu::Maybe<int>					componentGather;
81	bool							useSamplerSwizzleHint;
82
83	// Pseudorandom elements.
84	tcu::Vec2						textureCoordinates;
85	tcu::Maybe<VkClearColorValue>	customBorderColor;
86	bool							useStencilAspect;
87
88	bool isCustom (void) const
89	{
90		return (borderColor == VK_BORDER_COLOR_INT_CUSTOM_EXT || borderColor == VK_BORDER_COLOR_FLOAT_CUSTOM_EXT);
91	}
92
93	bool isOpaqueBlack (void) const
94	{
95		return (borderColor == VK_BORDER_COLOR_INT_OPAQUE_BLACK || borderColor == VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
96	}
97
98	bool isIdentity (void) const
99	{
100		return isIdentitySwizzle(componentMapping);
101	}
102};
103
104struct SpecConstants
105{
106	float	u;
107	float	v;
108	deInt32	gatherFlag;
109	//deInt32	gatherComp;
110};
111
112class BorderSwizzleCase : public vkt::TestCase
113{
114public:
115							BorderSwizzleCase		(tcu::TestContext& testCtx, const std::string& name, const TestParams& params);
116	virtual					~BorderSwizzleCase		(void) {}
117
118	virtual void			initPrograms			(vk::SourceCollections& programCollection) const;
119	virtual TestInstance*	createInstance			(Context& context) const;
120	virtual void			checkSupport			(Context& context) const;
121
122protected:
123	TestParams				m_params;
124};
125
126class BorderSwizzleInstance : public vkt::TestInstance
127{
128public:
129								BorderSwizzleInstance	(Context& context, const TestParams &params);
130	virtual						~BorderSwizzleInstance	(void) {}
131
132	VkExtent3D					getImageExtent			(void) const;
133	virtual tcu::TestStatus		iterate					(void);
134
135protected:
136	TestParams				m_params;
137};
138
139BorderSwizzleCase::BorderSwizzleCase(tcu::TestContext& testCtx, const std::string& name, const TestParams& params)
140	: vkt::TestCase	(testCtx, name)
141	, m_params		(params)
142{
143}
144
145void BorderSwizzleCase::checkSupport (Context& context) const
146{
147	const auto&				vki					= context.getInstanceInterface();
148	const auto				physicalDevice		= context.getPhysicalDevice();
149	VkImageFormatProperties	formatProperties;
150
151#ifndef CTS_USES_VULKANSC
152	if (m_params.textureFormat == VK_FORMAT_A8_UNORM_KHR || m_params.textureFormat == VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR)
153		context.requireDeviceFunctionality("VK_KHR_maintenance5");
154#endif // CTS_USES_VULKANSC
155
156	const auto result = vki.getPhysicalDeviceImageFormatProperties(
157		physicalDevice, m_params.textureFormat, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT), 0u, &formatProperties);
158
159	if (result != VK_SUCCESS)
160	{
161		if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
162			TCU_THROW(NotSupportedError, "Format not supported for sampling");
163		TCU_FAIL("vkGetPhysicalDeviceImageFormatProperties returned " + de::toString(result));
164	}
165
166	const auto&	borderColorFeatures		= context.getCustomBorderColorFeaturesEXT();
167	const auto&	borderSwizzleFeatures	= context.getBorderColorSwizzleFeaturesEXT();
168	const bool	identity				= m_params.isIdentity();
169
170	if (m_params.useSamplerSwizzleHint)
171		context.requireDeviceFunctionality("VK_EXT_border_color_swizzle");
172
173	// VK_COMPONENT_SWIZZLE_ONE is undefined when used with combined depth stencil formats, unless the maintenance5 property 'depthStencilSwizzleOneSupport' is supported
174	// For depth/stencil formats, VK_COMPONENT_SWIZZLE_A is aliased to VK_COMPONENT_SWIZZLE_ONE within this test group.
175	if (isCombinedDepthStencilType(mapVkFormat(m_params.textureFormat).type) && (
176			(m_params.componentMapping.r == VK_COMPONENT_SWIZZLE_ONE) || (m_params.componentMapping.r == VK_COMPONENT_SWIZZLE_A) ||
177			(m_params.componentMapping.g == VK_COMPONENT_SWIZZLE_ONE) || (m_params.componentMapping.g == VK_COMPONENT_SWIZZLE_A) ||
178			(m_params.componentMapping.b == VK_COMPONENT_SWIZZLE_ONE) || (m_params.componentMapping.b == VK_COMPONENT_SWIZZLE_A) ||
179			(m_params.componentMapping.a == VK_COMPONENT_SWIZZLE_ONE) || (m_params.componentMapping.a == VK_COMPONENT_SWIZZLE_A)
180		))
181	{
182		context.requireDeviceFunctionality("VK_KHR_maintenance5");
183
184		if (!context.getMaintenance5Properties().depthStencilSwizzleOneSupport)
185			TCU_THROW(NotSupportedError, "Swizzle results are undefined without depthStencilSwizzleOneSupport");
186	}
187
188	checkPipelineConstructionRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_params.pipelineConstructionType);
189
190	if (m_params.isCustom())
191	{
192		if (!borderColorFeatures.customBorderColors)
193			TCU_THROW(NotSupportedError, "Custom border colors not supported");
194
195		if (!identity)
196		{
197			if (!borderSwizzleFeatures.borderColorSwizzle)
198				TCU_THROW(NotSupportedError, "Custom border color with non-identity swizzle not supported");
199
200			if (!m_params.useSamplerSwizzleHint && !borderSwizzleFeatures.borderColorSwizzleFromImage)
201				TCU_THROW(NotSupportedError, "Custom border color with non-identity swizzle not supported without specifying sampler border mapping");
202		}
203	}
204	else if (m_params.isOpaqueBlack())
205	{
206		if (!identity)
207		{
208			if (!borderSwizzleFeatures.borderColorSwizzle)
209				TCU_THROW(NotSupportedError, "Opaque black with non-identity swizzle not supported");
210
211			if (!m_params.useSamplerSwizzleHint && !borderSwizzleFeatures.borderColorSwizzleFromImage)
212				TCU_THROW(NotSupportedError, "Opaque black with non-identity swizzle not supported without specifying sampler border mapping");
213		}
214	}
215}
216
217enum class FormatType
218{
219	SIGNED_INT = 0,
220	UNSIGNED_INT,
221	FLOAT,
222};
223
224FormatType getFormatType (VkFormat format, bool useStencil)
225{
226	if (isIntFormat(format))
227		return FormatType::SIGNED_INT;
228
229	if (isUintFormat(format) || useStencil)
230		return FormatType::UNSIGNED_INT;
231
232	return FormatType::FLOAT;
233}
234
235// Output color attachment format will vary slightly with the chosen texture format to accomodate different clear colors.
236VkFormat getColorAttachmentFormat (VkFormat textureFormat, bool useStencil)
237{
238	const auto formatType = getFormatType(textureFormat, useStencil);
239
240	if (formatType == FormatType::SIGNED_INT)
241		return VK_FORMAT_R32G32B32A32_SINT;
242
243	if (formatType == FormatType::UNSIGNED_INT)
244		return VK_FORMAT_R32G32B32A32_UINT;
245
246	return VK_FORMAT_R32G32B32A32_SFLOAT;
247}
248
249void BorderSwizzleCase::initPrograms (vk::SourceCollections& programCollection) const
250{
251	std::ostringstream vert;
252	vert
253		<< "#version 450\n"
254		<< "\n"
255		<< "void main()\n"
256		<< "{\n"
257		// Full-screen clockwise triangle strip with 4 vertices.
258		<< "	const float x = (-1.0+2.0*((gl_VertexIndex & 2)>>1));\n"
259		<< "	const float y = ( 1.0-2.0* (gl_VertexIndex % 2));\n"
260		<< "	gl_Position = vec4(x, y, 0.0, 1.0);\n"
261		<< "}\n"
262		;
263
264	const auto formatType = getFormatType(m_params.textureFormat, m_params.useStencilAspect);
265
266	std::string	prefix;
267	if (formatType == FormatType::SIGNED_INT)
268		prefix = "i";
269	else if (formatType == FormatType::UNSIGNED_INT)
270		prefix = "u";
271
272	const std::string	samplerType		= prefix + "sampler2D";
273	const std::string	outColorType	= prefix + "vec4";
274	// Note: glslang will complain if the gather component is not a compile-time constant.
275	const int			gatherComp		= (m_params.componentGather ? m_params.componentGather.get() : 0);
276
277	// Note the spec constants here should match the SpecConstants structure.
278	std::ostringstream frag;
279	frag
280		<< "#version 450\n"
281		<< "\n"
282		<< "layout (constant_id=0) const float u = 0.0f;\n"
283		<< "layout (constant_id=1) const float v = 0.0f;\n"
284		<< "layout (constant_id=2) const int gatherFlag = 0;\n"
285		//<< "layout (constant_id=3) const int gatherComp = 0;\n"
286		<< "\n"
287		<< "layout (set=0, binding=0) uniform " << samplerType << " texSampler;\n"
288		<< "\n"
289		<< "layout (location=0) out " << outColorType << " colorOut;\n"
290		<< "\n"
291		<< "void main()\n"
292		<< "{\n"
293		<< "	const vec2 coords = vec2(u, v);\n"
294		<< "\n"
295		<< "	if (gatherFlag != 0)\n"
296		<< "	{\n"
297		<< "		colorOut = textureGather(texSampler, coords, " << gatherComp << ");\n"
298		<< "	}\n"
299		<< "	else\n"
300		<< "	{\n"
301		<< "		colorOut = texture(texSampler, coords);\n"
302		<< "	}\n"
303		<< "}\n"
304		;
305
306	programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
307	programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
308}
309
310TestInstance* BorderSwizzleCase::createInstance (Context& context) const
311{
312	return new BorderSwizzleInstance(context, m_params);
313}
314
315BorderSwizzleInstance::BorderSwizzleInstance (Context& context, const TestParams &params)
316	: vkt::TestInstance	(context)
317	, m_params			(params)
318{}
319
320VkExtent3D BorderSwizzleInstance::getImageExtent (void) const
321{
322	return makeExtent3D(16u, 16u, 1u);
323}
324
325// Reinterprets the exponent and mantissa in the floating point number as an integer.
326// Function copied from vktApiImageClearingTests.cpp but changed return type to deUint64.
327deUint64 calcFloatDiff (float a, float b)
328{
329	const int		asign	= tcu::Float32(a).sign();
330	const int		bsign	= tcu::Float32(a).sign();
331
332	const deUint32	avalue	= (tcu::Float32(a).bits() & ((0x1u << 31u) - 1u));
333	const deUint32	bvalue	= (tcu::Float32(b).bits() & ((0x1u << 31u) - 1u));
334
335	if (asign != bsign)
336		return avalue + bvalue + 1u;
337	else if (avalue < bvalue)
338		return bvalue - avalue;
339	else
340		return avalue - bvalue;
341}
342
343// Convert VkComponentMapping to an array of 4 VkComponentSwizzle elements.
344tcu::Vector<VkComponentSwizzle, 4> makeComponentSwizzleVec(const VkComponentMapping& mapping)
345{
346	const tcu::Vector<VkComponentSwizzle, 4> result = {{ mapping.r, mapping.g, mapping.b, mapping.a }};
347	return result;
348}
349
350// Apply swizzling to an array of 4 elements.
351template <typename T>
352tcu::Vector<T, 4> applySwizzle (const tcu::Vector<T, 4>& orig, const VkComponentMapping& mapping)
353{
354	const auto			swizzles	= makeComponentSwizzleVec(mapping);
355	tcu::Vector<T, 4>	result;
356
357	for (int i = 0; i < decltype(swizzles)::SIZE; ++i)
358	{
359		const auto cs = swizzles[i];
360		DE_ASSERT(cs >= VK_COMPONENT_SWIZZLE_IDENTITY && cs <= VK_COMPONENT_SWIZZLE_A);
361
362		if (cs == VK_COMPONENT_SWIZZLE_IDENTITY)
363			result[i] = orig[i];
364		else if (cs == VK_COMPONENT_SWIZZLE_ZERO)
365			result[i] = static_cast<T>(0);
366		else if (cs == VK_COMPONENT_SWIZZLE_ONE)
367			result[i] = static_cast<T>(1);
368		else
369			result[i] = orig[cs - VK_COMPONENT_SWIZZLE_R];
370	}
371
372	return result;
373}
374
375// Apply gathering to an array of 4 elements.
376template <typename T>
377tcu::Vector<T, 4> applyGather (const tcu::Vector<T, 4>& orig, int compNum)
378{
379	tcu::Vector<T, 4> result;
380
381	for (int i = 0; i < decltype(result)::SIZE; ++i)
382		result[i] = orig[compNum];
383
384	return result;
385}
386
387// Transforms an input border color, once expanded, to the expected output color.
388template <typename T>
389tcu::Vector<T, 4> getExpectedColor (const tcu::Vector<T, 4>& color, const TestParams& params)
390{
391	tcu::Vector<T, 4> result = color;
392
393	result = applySwizzle(result, params.componentMapping);
394
395	if (params.componentGather)
396		result = applyGather(result, *params.componentGather);
397
398	return result;
399}
400
401// Transforms an input border color to the expected output color.
402// Uses the proper union member depending on the test parameters and takes into account "Conversion to RGBA" from the spec.
403VkClearColorValue getExpectedColor (const VkClearColorValue& color, const TestParams& params)
404{
405	const auto			tcuFormat	= mapVkFormat(params.textureFormat);
406	const auto			numComp		= tcu::getNumUsedChannels(tcuFormat.order);
407	const auto			formatType	= getFormatType(params.textureFormat, params.useStencilAspect);
408	VkClearColorValue	result;
409
410	DE_ASSERT(numComp >= 0 && numComp <= 4);
411
412	if (tcu::hasDepthComponent(tcuFormat.order) || tcu::hasStencilComponent(tcuFormat.order))
413	{
414		if (params.useStencilAspect)
415		{
416			tcu::UVec4 borderColor (0u, 0u, 0u, 1u);
417			borderColor[0] = color.uint32[0];
418			const auto expected = getExpectedColor(borderColor, params);
419
420			for (int i = 0; i < decltype(expected)::SIZE; ++i)
421				result.uint32[i] = expected[i];
422		}
423		else
424		{
425			tcu::Vec4 borderColor (0.0f, 0.0f, 0.0f, 1.0f);
426			borderColor[0] = color.float32[0];
427
428			const auto expected = getExpectedColor(borderColor, params);
429			for (int i = 0; i < decltype(expected)::SIZE; ++i)
430				result.float32[i] = expected[i];
431		}
432	}
433	else if (formatType == FormatType::UNSIGNED_INT)
434	{
435		tcu::UVec4 borderColor (0u, 0u, 0u, 0u);
436
437		for (int i = 0; i < numComp; ++i)
438			borderColor[i] = color.uint32[i];
439
440		if (numComp < 4)
441			borderColor[3] = 1u;
442
443		const auto expected = getExpectedColor(borderColor, params);
444
445		for (int i = 0; i < decltype(expected)::SIZE; ++i)
446			result.uint32[i] = expected[i];
447	}
448	else if (formatType == FormatType::SIGNED_INT)
449	{
450		tcu::IVec4 borderColor (0, 0, 0, 0);
451
452		for (int i = 0; i < numComp; ++i)
453			borderColor[i] = color.int32[i];
454
455		if (numComp < 4)
456			borderColor[3] = 1;
457
458		const auto expected = getExpectedColor(borderColor, params);
459
460		for (int i = 0; i < decltype(expected)::SIZE; ++i)
461			result.int32[i] = expected[i];
462	}
463	else
464	{
465		DE_ASSERT(formatType == FormatType::FLOAT);
466
467		tcu::Vec4 borderColor (.0f, .0f, .0f, 1.f);
468
469#ifndef CTS_USES_VULKANSC
470		if (params.textureFormat == VK_FORMAT_A8_UNORM_KHR)
471		{
472			// This one is a bit special compared to others we test. Single component alpha format borders use [0,0,0,Ba] as the
473			// border texel components after replacing (Ba being the border alpha component).
474			borderColor[3] = color.float32[3];
475		}
476		else
477#endif // CTS_USES_VULKANSC
478		{
479			// Other formats use the first color components from the border, and are expanded to 4 components by filling missing
480			// components with zero and the alpha component with 1.
481			for (int i = 0; i < numComp; ++i)
482				borderColor[i] = color.float32[i];
483		}
484
485		const auto expected = getExpectedColor(borderColor, params);
486
487		for (int i = 0; i < decltype(expected)::SIZE; ++i)
488			result.float32[i] = expected[i];
489	}
490
491	return result;
492}
493
494// Compare color buffer to the expected border color.
495//
496// This method was copied from vktApiImageClearingTests.cpp and adapted to this use case:
497//
498// * Taking into account the texture format instead of the color buffer format when calculating acceptable thresholds.
499// * Applying swizzles and gathering to said thresholds.
500// * Making thresholds more strict for components that do not come from custom borders.
501// * Checking the full image in a single pass.
502//
503// The color buffer format is supposed to be at least as precise as the texture format.
504bool comparePixelToColorClearValue (const TestParams&					params,
505									const tcu::ConstPixelBufferAccess&	access,
506									const tcu::TextureFormat&			textureFormat_,
507									const VkClearColorValue&			ref,
508									std::string&						stringResult)
509{
510	const auto	bufferFormat	= access.getFormat();
511	tcu::TextureFormat	textureFormat;
512
513	if (isCombinedDepthStencilType(textureFormat_.type))
514	{
515		// Verification loop does not support reading from combined depth stencil texture levels.
516		// Get rid of stencil component.
517
518		tcu::TextureFormat::ChannelOrder	channelOrder	= tcu::TextureFormat::CHANNELORDER_LAST;
519		tcu::TextureFormat::ChannelType		channelType		= tcu::TextureFormat::CHANNELTYPE_LAST;
520
521		const auto	hasStencil	= params.useStencilAspect;
522
523		if (hasStencil)
524		{
525			channelOrder	= tcu::TextureFormat::S;
526			channelType		= tcu::TextureFormat::UNSIGNED_INT8;
527		}
528		else
529		{
530			channelOrder = tcu::TextureFormat::D;
531
532			switch (textureFormat_.type)
533			{
534			case tcu::TextureFormat::UNSIGNED_INT_16_8_8:
535				channelType = tcu::TextureFormat::UNORM_INT16;
536				break;
537			case tcu::TextureFormat::UNSIGNED_INT_24_8:
538			case tcu::TextureFormat::UNSIGNED_INT_24_8_REV:
539				channelType = tcu::TextureFormat::UNORM_INT24;
540				break;
541			case tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:
542				channelType = tcu::TextureFormat::FLOAT;
543				break;
544			default:
545				DE_FATAL("Unhandled texture format type in switch");
546			}
547		}
548
549		textureFormat = tcu::TextureFormat(channelOrder, channelType);
550	}
551	else
552	{
553		textureFormat = textureFormat_;
554	}
555
556	const auto	channelClass	= getTextureChannelClass(textureFormat.type);
557	// We must compare all available channels in the color buffer to check RGBA conversion.
558	const auto	channelMask		= getTextureFormatChannelMask(bufferFormat);
559	// If the component mapping contains a SWIZZLE_ONE, overwrite this with a SWIZZLE_ZERO to ensure
560	// a strict tolerance when applying a swizzle of SWIZZLE_ONE to the threshold.
561	const VkComponentMapping thresholdComponentMapping =
562	{
563		(params.componentMapping.r == VK_COMPONENT_SWIZZLE_ONE ? VK_COMPONENT_SWIZZLE_ZERO : params.componentMapping.r),
564		(params.componentMapping.g == VK_COMPONENT_SWIZZLE_ONE ? VK_COMPONENT_SWIZZLE_ZERO : params.componentMapping.g),
565		(params.componentMapping.b == VK_COMPONENT_SWIZZLE_ONE ? VK_COMPONENT_SWIZZLE_ZERO : params.componentMapping.b),
566		(params.componentMapping.a == VK_COMPONENT_SWIZZLE_ONE ? VK_COMPONENT_SWIZZLE_ZERO : params.componentMapping.a),
567	};
568
569	switch (channelClass)
570	{
571		case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
572		case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
573		{
574			tcu::Vec4			refColor	(ref.float32[0],
575											 ref.float32[1],
576											 ref.float32[2],
577											 ref.float32[3]);
578			tcu::Vec4			threshold	(0.0f);
579
580			if (params.isCustom())
581			{
582				// Relax thresholds for custom color components.
583				const tcu::IVec4	bitDepth	(getTextureFormatBitDepth(textureFormat));
584				const int			modifier	= (channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) ? 0 : 1;
585
586				threshold = tcu::Vec4 (bitDepth[0] > 0 ? 1.0f / ((float)(1 << (bitDepth[0] - modifier)) - 1.0f) : 0.0f,
587									   bitDepth[1] > 0 ? 1.0f / ((float)(1 << (bitDepth[1] - modifier)) - 1.0f) : 0.0f,
588									   bitDepth[2] > 0 ? 1.0f / ((float)(1 << (bitDepth[2] - modifier)) - 1.0f) : 0.0f,
589									   bitDepth[3] > 0 ? 1.0f / ((float)(1 << (bitDepth[3] - modifier)) - 1.0f) : 0.0f);
590
591				if (isSRGB(textureFormat))
592				{
593					// Widen thresholds a bit due to possible low-precision sRGB conversions.
594					for (int i = 0; i < decltype(threshold)::SIZE; ++i)
595						threshold[i] *= 2.0f;
596				}
597			}
598
599			// Apply swizzle and gather to thresholds.
600			threshold = applySwizzle(threshold, thresholdComponentMapping);
601
602			if (params.componentGather)
603				threshold = applyGather(threshold, *params.componentGather);
604
605			for (int z = 0; z < access.getDepth(); ++z)
606			for (int y = 0; y < access.getHeight(); ++y)
607			for (int x = 0; x < access.getWidth(); ++x)
608			{
609				const tcu::Vec4	resColor	(access.getPixel(x, y, z));
610				const bool		result		= !(anyNotEqual(logicalAnd(lessThanEqual(absDiff(resColor, refColor), threshold), channelMask), channelMask));
611
612				if (!result || (x == 0 && y == 0 && z == 0))
613				{
614					std::stringstream s;
615					s << "Ref:" << refColor << " Threshold:" << threshold << " Color:" << resColor;
616					stringResult = s.str();
617				}
618
619				if (!result)
620					return false;
621			}
622
623			return true;
624		}
625
626		case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
627		{
628			const tcu::UVec4	refColor	(ref.uint32[0],
629											 ref.uint32[1],
630											 ref.uint32[2],
631											 ref.uint32[3]);
632			tcu::UVec4			threshold	(0u);
633
634			if (params.isCustom())
635			{
636				// Relax thresholds for custom color components.
637				const tcu::IVec4 bitDepth (getTextureFormatBitDepth(textureFormat));
638
639				threshold = tcu::UVec4 ((bitDepth[0] > 0) ? 1 : 0,
640										(bitDepth[1] > 0) ? 1 : 0,
641										(bitDepth[2] > 0) ? 1 : 0,
642										(bitDepth[3] > 0) ? 1 : 0);
643			}
644
645			// Apply swizzle and gather to thresholds.
646			threshold = applySwizzle(threshold, thresholdComponentMapping);
647
648			if (params.componentGather)
649				threshold = applyGather(threshold, *params.componentGather);
650
651			for (int z = 0; z < access.getDepth(); ++z)
652			for (int y = 0; y < access.getHeight(); ++y)
653			for (int x = 0; x < access.getWidth(); ++x)
654			{
655				const tcu::UVec4	resColor	(access.getPixelUint(x, y, z));
656				const bool			result		= !(anyNotEqual(logicalAnd(lessThanEqual(absDiff(resColor, refColor), threshold), channelMask), channelMask));
657
658				if (!result || (x == 0 && y == 0 && z == 0))
659				{
660					std::stringstream s;
661					s << "Ref:" << refColor << " Threshold:" << threshold << " Color:" << resColor;
662					stringResult = s.str();
663				}
664
665				if (!result)
666					return false;
667			}
668
669			return true;
670		}
671
672		case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
673		{
674			const tcu::IVec4	refColor	(ref.int32[0],
675											 ref.int32[1],
676											 ref.int32[2],
677											 ref.int32[3]);
678			tcu::IVec4			threshold	(0);
679
680			if (params.isCustom())
681			{
682				// Relax thresholds for custom color components.
683				const tcu::IVec4 bitDepth (getTextureFormatBitDepth(textureFormat));
684
685				threshold = tcu::IVec4 ((bitDepth[0] > 0) ? 1 : 0,
686										(bitDepth[1] > 0) ? 1 : 0,
687										(bitDepth[2] > 0) ? 1 : 0,
688										(bitDepth[3] > 0) ? 1 : 0);
689			}
690
691			// Apply swizzle and gather to thresholds.
692			threshold = applySwizzle(threshold, thresholdComponentMapping);
693
694			if (params.componentGather)
695				threshold = applyGather(threshold, *params.componentGather);
696
697			for (int z = 0; z < access.getDepth(); ++z)
698			for (int y = 0; y < access.getHeight(); ++y)
699			for (int x = 0; x < access.getWidth(); ++x)
700			{
701				const tcu::IVec4	resColor	(access.getPixelInt(x, y, z));
702				const bool			result		= !(anyNotEqual(logicalAnd(lessThanEqual(absDiff(resColor, refColor), threshold), channelMask), channelMask));
703
704				if (!result || (x == 0 && y == 0 && z == 0))
705				{
706					std::stringstream s;
707					s << "Ref:" << refColor << " Threshold:" << threshold << " Color:" << resColor;
708					stringResult = s.str();
709				}
710
711				if (!result)
712					return false;
713			}
714
715			return true;
716		}
717
718		case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
719		{
720			using u64v4 = tcu::Vector<deUint64, 4>;
721
722			const tcu::Vec4		refColor		(ref.float32[0],
723												 ref.float32[1],
724												 ref.float32[2],
725												 ref.float32[3]);
726			u64v4				threshold		(0ull);
727
728			if (params.isCustom())
729			{
730				// Relax thresholds for custom color components.
731				const tcu::IVec4	mantissaBitsI	(getTextureFormatMantissaBitDepth(textureFormat));
732				const u64v4			mantissaBits	(mantissaBitsI.x(), mantissaBitsI.y(), mantissaBitsI.z(), mantissaBitsI.w());
733
734				threshold = u64v4 ((mantissaBits[0] > 0ull) ? 10ull * (1ull << (23ull - mantissaBits[0])) : 0ull,
735								   (mantissaBits[1] > 0ull) ? 10ull * (1ull << (23ull - mantissaBits[1])) : 0ull,
736								   (mantissaBits[2] > 0ull) ? 10ull * (1ull << (23ull - mantissaBits[2])) : 0ull,
737								   (mantissaBits[3] > 0ull) ? 10ull * (1ull << (23ull - mantissaBits[3])) : 0ull);
738			}
739
740			// Apply swizzle and gather to thresholds.
741			threshold = applySwizzle(threshold, thresholdComponentMapping);
742
743			if (params.componentGather)
744				threshold = applyGather(threshold, *params.componentGather);
745
746			DE_ASSERT(allEqual(greaterThanEqual(threshold, u64v4(0u)), tcu::BVec4(true)));
747
748			for (int z = 0; z < access.getDepth(); ++z)
749			for (int y = 0; y < access.getHeight(); ++y)
750			for (int x = 0; x < access.getWidth(); ++x)
751			{
752				const tcu::Vec4	resColor (access.getPixel(x, y, z));
753
754				for (int ndx = 0; ndx < decltype(resColor)::SIZE; ndx++)
755				{
756					const bool result = !(calcFloatDiff(resColor[ndx], refColor[ndx]) > threshold[ndx] && channelMask[ndx]);
757
758					if (!result || (x == 0 && y == 0 && z == 0))
759					{
760						float				floatThreshold	= tcu::Float32((deUint32)(threshold)[0]).asFloat();
761						tcu::Vec4			thresholdVec4	(floatThreshold,
762															 floatThreshold,
763															 floatThreshold,
764															 floatThreshold);
765						std::stringstream	s;
766
767						s << "Ref:" << refColor << " Threshold:" << thresholdVec4 << " Color:" << resColor;
768						stringResult = s.str();
769					}
770
771					if (!result)
772						return false;
773				}
774			}
775
776			return true;
777		}
778
779		default:
780			DE_FATAL("Invalid channel class");
781			return false;
782	}
783}
784
785// Gets the clear color value from the border color. See "Texel Replacement" in the spec.
786VkClearColorValue getBorderClearColorValue (const TestParams& params)
787{
788	VkClearColorValue result;
789	deMemset(&result, 0, sizeof(result));
790
791	switch (params.borderColor)
792	{
793	case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:	/* memset works. */															break;
794	case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:		/* memset works. */															break;
795	case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:		result.float32[3] = 1.0f;													break;
796	case VK_BORDER_COLOR_INT_OPAQUE_BLACK:			result.int32[3] = 1;														break;
797	case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:		for (size_t i = 0; i < 4; ++i) result.float32[i] = 1.0f;					break;
798	case VK_BORDER_COLOR_INT_OPAQUE_WHITE:			for (size_t i = 0; i < 4; ++i) result.int32[i] = 1;							break;
799	case VK_BORDER_COLOR_FLOAT_CUSTOM_EXT:			// fallthrough.
800	case VK_BORDER_COLOR_INT_CUSTOM_EXT:			DE_ASSERT(params.customBorderColor); result = *params.customBorderColor;	break;
801	default:										DE_ASSERT(false);															break;
802	}
803
804	return result;
805}
806
807tcu::TestStatus BorderSwizzleInstance::iterate (void)
808{
809	const auto&	vki						= m_context.getInstanceInterface();
810	const auto&	vkd						= m_context.getDeviceInterface();
811	const auto	physicalDevice			= m_context.getPhysicalDevice();
812	const auto	device					= m_context.getDevice();
813	auto&		alloc					= m_context.getDefaultAllocator();
814	const auto	queue					= m_context.getUniversalQueue();
815	const auto	qIndex					= m_context.getUniversalQueueFamilyIndex();
816	const auto	extent					= getImageExtent();
817	const auto	custom					= m_params.isCustom();
818	const auto	isDSFormat				= isDepthStencilFormat(m_params.textureFormat);
819	const auto	hasStencil				= m_params.useStencilAspect;
820	const auto	imageAspect				= (isDSFormat ? (hasStencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT) : VK_IMAGE_ASPECT_COLOR_BIT);
821	const auto	imageSubresourceRange	= makeImageSubresourceRange(imageAspect, 0u, 1u, 0u, 1u);
822	const auto	colorAttachmentFormat	= getColorAttachmentFormat(m_params.textureFormat, hasStencil);
823	const auto	colorSubresourceRange	= makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
824
825	// Texture.
826	const VkImageCreateInfo textureCreateInfo =
827	{
828		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType			sType;
829		nullptr,								//	const void*				pNext;
830		0u,										//	VkImageCreateFlags		flags;
831		VK_IMAGE_TYPE_2D,						//	VkImageType				imageType;
832		m_params.textureFormat,					//	VkFormat				format;
833		extent,									//	VkExtent3D				extent;
834		1u,										//	deUint32				mipLevels;
835		1u,										//	deUint32				arrayLayers;
836		VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits	samples;
837		VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling			tiling;
838		(VK_IMAGE_USAGE_SAMPLED_BIT				//	VkImageUsageFlags		usage;
839		|VK_IMAGE_USAGE_TRANSFER_DST_BIT),
840		VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode			sharingMode;
841		0u,										//	deUint32				queueFamilyIndexCount;
842		nullptr,								//	const deUint32*			pQueueFamilyIndices;
843		VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout			initialLayout;
844	};
845
846	ImageWithMemory texture (vkd, device, alloc, textureCreateInfo, MemoryRequirement::Any);
847
848	const VkImageViewCreateInfo textureViewCreateInfo =
849	{
850		VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,	//	VkStructureType			sType;
851		nullptr,									//	const void*				pNext;
852		0u,											//	VkImageViewCreateFlags	flags;
853		texture.get(),								//	VkImage					image;
854		VK_IMAGE_VIEW_TYPE_2D,						//	VkImageViewType			viewType;
855		m_params.textureFormat,						//	VkFormat				format;
856		m_params.componentMapping,					//	VkComponentMapping		components;
857		imageSubresourceRange,						//	VkImageSubresourceRange	subresourceRange;
858	};
859
860	const auto textureView = createImageView(vkd, device, &textureViewCreateInfo);
861
862	// Color attachment.
863	const VkImageCreateInfo colorAttachmentInfo =
864	{
865		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType			sType;
866		nullptr,								//	const void*				pNext;
867		0u,										//	VkImageCreateFlags		flags;
868		VK_IMAGE_TYPE_2D,						//	VkImageType				imageType;
869		colorAttachmentFormat,					//	VkFormat				format;
870		extent,									//	VkExtent3D				extent;
871		1u,										//	deUint32				mipLevels;
872		1u,										//	deUint32				arrayLayers;
873		VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits	samples;
874		VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling			tiling;
875		(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
876		| VK_IMAGE_USAGE_TRANSFER_SRC_BIT),		//	VkImageUsageFlags		usage;
877		VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode			sharingMode;
878		0u,										//	deUint32				queueFamilyIndexCount;
879		nullptr,								//	const deUint32*			pQueueFamilyIndices;
880		VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout			initialLayout;
881	};
882
883	ImageWithMemory colorAttachment (vkd, device, alloc, colorAttachmentInfo, MemoryRequirement::Any);
884
885	const auto colorAttachmentView = makeImageView(vkd, device, colorAttachment.get(), VK_IMAGE_VIEW_TYPE_2D, colorAttachmentInfo.format, colorSubresourceRange);
886
887	// Texure sampler.
888	de::MovePtr<VkSamplerCustomBorderColorCreateInfoEXT> customBorderColorInfo;
889
890	const VkSamplerBorderColorComponentMappingCreateInfoEXT borderColorMappingInfo =
891	{
892		VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT,
893		nullptr,
894		m_params.componentMapping,
895		isSrgbFormat(m_params.textureFormat),
896	};
897
898	const void* pNext = nullptr;
899
900	if (custom)
901	{
902		customBorderColorInfo	= de::MovePtr<VkSamplerCustomBorderColorCreateInfoEXT>(new VkSamplerCustomBorderColorCreateInfoEXT);
903		*customBorderColorInfo	= initVulkanStructure();
904
905		DE_ASSERT(m_params.customBorderColor);
906		VkClearColorValue colorValue = m_params.customBorderColor.get();
907
908		if (m_params.useSamplerSwizzleHint)
909			customBorderColorInfo->pNext = &borderColorMappingInfo;
910
911		// TODO: try combinations with customBorderColorWithoutFormat if supported?
912		customBorderColorInfo->format				= m_params.textureFormat;
913		customBorderColorInfo->customBorderColor	= colorValue;
914
915		pNext = customBorderColorInfo.get();
916	}
917	else
918	{
919		if (m_params.useSamplerSwizzleHint)
920			pNext = &borderColorMappingInfo;
921	}
922
923	const VkSamplerCreateInfo samplerCreateInfo =
924	{
925		VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,				//	VkStructureType			sType;
926		pNext,												//	const void*				pNext;
927		0u,													//	VkSamplerCreateFlags	flags;
928		VK_FILTER_NEAREST,									//	VkFilter				magFilter;
929		VK_FILTER_NEAREST,									//	VkFilter				minFilter;
930		VK_SAMPLER_MIPMAP_MODE_NEAREST,						//	VkSamplerMipmapMode		mipmapMode;
931		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,			//	VkSamplerAddressMode	addressModeU;
932		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,			//	VkSamplerAddressMode	addressModeV;
933		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,			//	VkSamplerAddressMode	addressModeW;
934		0u,													//	float					mipLodBias;
935		VK_FALSE,											//	VkBool32				anisotropyEnable;
936		0.0f,												//	float					maxAnisotropy;
937		VK_FALSE,											//	VkBool32				compareEnable;
938		VK_COMPARE_OP_NEVER,								//	VkCompareOp				compareOp;
939		0.0f,												//	float					minLod;
940		1.0f,												//	float					maxLod;
941		m_params.borderColor,								//	VkBorderColor			borderColor;
942		VK_FALSE,											//	VkBool32				unnormalizedCoordinates;
943	};
944
945	const auto sampler = createSampler(vkd, device, &samplerCreateInfo);
946
947	// Descriptor set layout.
948	DescriptorSetLayoutBuilder dsLayoutBuilder;
949	dsLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT);
950	const auto dsLayout = dsLayoutBuilder.build(vkd, device);
951
952	// Pipeline layout.
953	const PipelineLayoutWrapper pipelineLayout (m_params.pipelineConstructionType, vkd, device, dsLayout.get());
954
955	// Descriptor pool.
956	DescriptorPoolBuilder poolBuilder;
957	poolBuilder.addType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
958	const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
959
960	// Descriptor set.
961	const auto descriptorSet = makeDescriptorSet(vkd, device, descriptorPool.get(), dsLayout.get());
962
963	// Update descriptor set.
964	{
965		DescriptorSetUpdateBuilder updateBuilder;
966		VkDescriptorImageInfo descriptorImageInfo = makeDescriptorImageInfo(sampler.get(), textureView.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
967		updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &descriptorImageInfo);
968		updateBuilder.update(vkd, device);
969	}
970
971	// Render pass.
972	RenderPassWrapper renderPass (m_params.pipelineConstructionType, vkd, device, colorAttachmentFormat);
973
974	// Shader modules.
975	const auto vertShader = ShaderWrapper(vkd, device, m_context.getBinaryCollection().get("vert"), 0u);
976	const auto fragShader = ShaderWrapper(vkd, device, m_context.getBinaryCollection().get("frag"), 0u);
977
978	const SpecConstants specConstantData =
979	{
980		m_params.textureCoordinates.x(),
981		m_params.textureCoordinates.y(),
982		(m_params.componentGather ? 1 : 0),
983		//(m_params.componentGather ? *m_params.componentGather : -1),
984	};
985
986	const VkSpecializationMapEntry specializationMap[] =
987	{
988		{	0u, offsetof(SpecConstants, u),				sizeof(specConstantData.u)			},
989		{	1u, offsetof(SpecConstants, v),				sizeof(specConstantData.v)			},
990		{	2u, offsetof(SpecConstants, gatherFlag),	sizeof(specConstantData.gatherFlag)	},
991		//{	3u, offsetof(SpecConstants, gatherComp),	sizeof(specConstantData.gatherComp)	},
992	};
993
994	const VkSpecializationInfo specializationInfo =
995	{
996		static_cast<deUint32>(DE_LENGTH_OF_ARRAY(specializationMap)),	//	deUint32						mapEntryCount;
997		specializationMap,												//	const VkSpecializationMapEntry*	pMapEntries;
998		static_cast<deUintptr>(sizeof(specConstantData)),				//	deUintptr						dataSize;
999		&specConstantData,												//	const void*						pData;
1000	};
1001
1002	const VkPipelineVertexInputStateCreateInfo vertexInputInfo = initVulkanStructure();
1003
1004	const std::vector<VkViewport>	viewport	{ makeViewport(extent) };
1005	const std::vector<VkRect2D>		scissor		{ makeRect2D(extent) };
1006
1007	VkPipelineColorBlendAttachmentState colorBlendAttachmentState;
1008	deMemset(&colorBlendAttachmentState, 0, sizeof(colorBlendAttachmentState));
1009	colorBlendAttachmentState.colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);
1010
1011	const VkPipelineColorBlendStateCreateInfo colorBlendInfo
1012	{
1013		VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,	//	VkStructureType								sType;
1014		nullptr,													//	const void*									pNext;
1015		0u,															//	VkPipelineColorBlendStateCreateFlags		flags;
1016		VK_FALSE,													//	VkBool32									logicOpEnable;
1017		VK_LOGIC_OP_CLEAR,											//	VkLogicOp									logicOp;
1018		1u,															//	deUint32									attachmentCount;
1019		&colorBlendAttachmentState,									//	const VkPipelineColorBlendAttachmentState*	pAttachments;
1020		{ .0f, .0f, .0f, .0f },										//	float										blendConstants[4];
1021	};
1022
1023	GraphicsPipelineWrapper graphicsPipeline(vki, vkd, physicalDevice, device, m_context.getDeviceExtensions(), m_params.pipelineConstructionType);
1024	graphicsPipeline.setDefaultTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)
1025					.setDefaultDepthStencilState()
1026					.setDefaultRasterizationState()
1027					.setDefaultMultisampleState()
1028					.setupVertexInputState(&vertexInputInfo)
1029					.setupPreRasterizationShaderState(viewport,
1030									scissor,
1031									pipelineLayout,
1032									*renderPass,
1033									0u,
1034									vertShader)
1035					.setupFragmentShaderState(pipelineLayout,
1036									*renderPass,
1037									0u,
1038									fragShader,
1039									DE_NULL,
1040									DE_NULL,
1041									&specializationInfo)
1042					.setupFragmentOutputState(*renderPass, 0u, &colorBlendInfo)
1043					.setMonolithicPipelineLayout(pipelineLayout)
1044					.buildPipeline();
1045
1046	// Framebuffer.
1047	renderPass.createFramebuffer(vkd, device, colorAttachment.get(), colorAttachmentView.get(), extent.width, extent.height);
1048
1049	// Command pool and buffer.
1050	const auto cmdPool		= makeCommandPool(vkd, device, qIndex);
1051	const auto cmdBufferPtr	= allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
1052	const auto cmdBuffer	= cmdBufferPtr.get();
1053
1054	// Empty clear color for the framebuffer.
1055	VkClearValue zeroClearColor;
1056	deMemset(&zeroClearColor, 0, sizeof(zeroClearColor));
1057
1058	// Texture barriers to fill it before using it.
1059	const auto preClearBarrier = makeImageMemoryBarrier(
1060		0u,
1061		VK_ACCESS_TRANSFER_WRITE_BIT,
1062		VK_IMAGE_LAYOUT_UNDEFINED,
1063		VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1064		texture.get(),
1065		imageSubresourceRange);
1066
1067	const auto postClearBarrier = makeImageMemoryBarrier(
1068		VK_ACCESS_TRANSFER_WRITE_BIT,
1069		VK_ACCESS_SHADER_READ_BIT,
1070		VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1071		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
1072		texture.get(),
1073		imageSubresourceRange);
1074
1075	// Record and submit.
1076	beginCommandBuffer(vkd, cmdBuffer);
1077
1078	// Prepare texture.
1079	vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preClearBarrier);
1080	if (isDSFormat)
1081		vkd.cmdClearDepthStencilImage(cmdBuffer, texture.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &m_params.textureDepthStencilValue, 1u, &imageSubresourceRange);
1082	else
1083		vkd.cmdClearColorImage(cmdBuffer, texture.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &m_params.textureColor, 1u, &imageSubresourceRange);
1084	vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &postClearBarrier);
1085
1086	// Read from the texture to render a full-screen quad to the color buffer.
1087	renderPass.begin(vkd, cmdBuffer, scissor[0], zeroClearColor);
1088	graphicsPipeline.bind(cmdBuffer);
1089	vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
1090	vkd.cmdDraw(cmdBuffer, 4u, 1u, 0u, 0u);
1091	renderPass.end(vkd, cmdBuffer);
1092
1093	endCommandBuffer(vkd, cmdBuffer);
1094	submitCommandsAndWait(vkd, device, queue, cmdBuffer);
1095
1096	// Verify color buffer.
1097	const auto	renderSize				= tcu::UVec2(extent.width, extent.height);
1098	const auto	colorAttachmentLevel	= readColorAttachment(vkd, device, queue, qIndex, alloc, colorAttachment.get(), colorAttachmentFormat, renderSize);
1099	const auto	colorPixels				= colorAttachmentLevel->getAccess();
1100	const auto	tcuTextureFormat		= mapVkFormat(m_params.textureFormat);
1101	const auto	borderColor				= getBorderClearColorValue(m_params);
1102	const auto	expectedColor			= getExpectedColor(borderColor, m_params);
1103	std::string	resultMsg;
1104
1105	if (!comparePixelToColorClearValue(m_params, colorPixels, tcuTextureFormat, expectedColor, resultMsg))
1106		TCU_FAIL(resultMsg);
1107
1108	return tcu::TestStatus::pass(resultMsg);
1109}
1110
1111using ComponentSwizzleArray = std::array<VkComponentSwizzle, 4>;
1112
1113// Convert the component swizzle array to a component mapping structure.
1114void makeComponentMapping(VkComponentMapping& mapping, const ComponentSwizzleArray& array)
1115{
1116	mapping.r = array[0];
1117	mapping.g = array[1];
1118	mapping.b = array[2];
1119	mapping.a = array[3];
1120}
1121
1122std::string swizzleArrayToString(const ComponentSwizzleArray& swizzles)
1123{
1124	std::ostringstream stream;
1125
1126	for (const auto& s : swizzles)
1127	{
1128		switch (s)
1129		{
1130		case VK_COMPONENT_SWIZZLE_IDENTITY:	stream << "i"; break;
1131		case VK_COMPONENT_SWIZZLE_ZERO:		stream << "0"; break;
1132		case VK_COMPONENT_SWIZZLE_ONE:		stream << "1"; break;
1133		case VK_COMPONENT_SWIZZLE_R:		stream << "r"; break;
1134		case VK_COMPONENT_SWIZZLE_G:		stream << "g"; break;
1135		case VK_COMPONENT_SWIZZLE_B:		stream << "b"; break;
1136		case VK_COMPONENT_SWIZZLE_A:		stream << "a"; break;
1137		default:
1138			DE_ASSERT(false); break;
1139		}
1140	}
1141
1142	return stream.str();
1143}
1144
1145// Generate mapping permutations for the swizzle components.
1146// Note: using every permutation for component swizzle values results in 7^4=2401 combinations, which are too many.
1147std::vector<ComponentSwizzleArray> genMappingPermutations ()
1148{
1149	std::vector<ComponentSwizzleArray>	result;
1150	const ComponentSwizzleArray			standardSwizzle	= {{ VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }};
1151
1152	// Standard normal swizzle.
1153	result.push_back(standardSwizzle);
1154
1155	// Add a few combinations with rotated swizzles.
1156	for (size_t rotations = 1u; rotations < standardSwizzle.size(); ++rotations)
1157	{
1158		ComponentSwizzleArray rotatedSwizzle = standardSwizzle;
1159		std::rotate(rotatedSwizzle.begin(), rotatedSwizzle.begin() + rotations, rotatedSwizzle.end());
1160		result.push_back(rotatedSwizzle);
1161	}
1162
1163	// Try placing each special value in each of the positions.
1164	VkComponentSwizzle specialSwizzles[] = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_ONE, VK_COMPONENT_SWIZZLE_ZERO };
1165	for (const auto& special : specialSwizzles)
1166	{
1167		for (size_t pos = 0; pos < standardSwizzle.size(); ++pos)
1168		{
1169			ComponentSwizzleArray newArray = standardSwizzle;
1170			newArray[pos] = special;
1171			result.push_back(newArray);
1172		}
1173	}
1174
1175	return result;
1176}
1177
1178std::string gatherIndexToString(int gatherIndex)
1179{
1180	if (gatherIndex < 0)
1181		return "no_gather";
1182	return "gather_" + std::to_string(gatherIndex);
1183}
1184
1185bool isIntegerBorder (VkBorderColor borderType)
1186{
1187	bool isInt = false;
1188	switch (borderType)
1189	{
1190	case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
1191	case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
1192	case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
1193	case VK_BORDER_COLOR_FLOAT_CUSTOM_EXT:
1194		isInt = false; break;
1195	case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:
1196	case VK_BORDER_COLOR_INT_OPAQUE_BLACK:
1197	case VK_BORDER_COLOR_INT_OPAQUE_WHITE:
1198	case VK_BORDER_COLOR_INT_CUSTOM_EXT:
1199		isInt = true; break;
1200	default:
1201		DE_ASSERT(false); break;
1202	}
1203
1204	return isInt;
1205}
1206
1207tcu::Vec2 getRandomBorderCoordinates (de::Random& rnd)
1208{
1209	tcu::Vec2 coords;
1210
1211	// Two bits to decide which coordinates will be out of range (at least one).
1212	const deUint32 outOfRangeMask = static_cast<deUint32>(rnd.getInt(1, 3));
1213
1214	for (int i = 0; i < 2; ++i)
1215	{
1216		// Each coord will be in the [0.0, 0.9] range if in range, [1.1, 5.0] or [-5.0, -1.1] if out of range.
1217		bool	outOfRange	= (outOfRangeMask & (1<<i));
1218		bool	negative	= (outOfRange && rnd.getBool());
1219		float	minCoord	= (outOfRange ? 1.1f : 0.0f);
1220		float	maxCoord	= (outOfRange ? 5.0f : 0.9f);
1221		float	value		= (negative ? -1.0f : 1.0f) * rnd.getFloat(minCoord, maxCoord);
1222
1223		coords[i] = value;
1224	}
1225
1226	return coords;
1227}
1228
1229// Generate a random clear color usable for the given format.
1230VkClearColorValue getRandomClearColor (VkFormat format, de::Random& rnd, bool useStencil)
1231{
1232	VkClearColorValue color;
1233	deMemset(&color, 0, sizeof(color));
1234
1235	const auto		tcuFormat		= mapVkFormat(format);
1236	const auto		numComponents	= !useStencil ? tcu::getNumUsedChannels(tcuFormat.order) : 1;
1237	const auto		formatType		= getFormatType(format, useStencil);
1238
1239	for (int i = 0; i < numComponents; ++i)
1240	{
1241		if (formatType == FormatType::SIGNED_INT || formatType == FormatType::UNSIGNED_INT)
1242		{
1243			const auto		componentSize	= !useStencil ? tcu::getChannelSize(tcuFormat.type) : 1;
1244
1245			DE_ASSERT(componentSize > 0);
1246
1247			const deUint64	mask			= (1ull << (componentSize*8)) - 1ull;
1248			const deUint64	signBit			= (1ull << (componentSize*8-1));
1249			const deUint64	signMask		= (~mask); // Used to extend the sign bit.
1250			const auto value = rnd.getUint64();
1251
1252			if (formatType == FormatType::SIGNED_INT)
1253			{
1254				// Extend sign bit for negative values.
1255				auto finalValue = (value & mask);
1256				if (finalValue & signBit)
1257					finalValue |= signMask;
1258				color.int32[i] = static_cast<deInt32>(finalValue);
1259			}
1260			else
1261				color.uint32[i] = static_cast<deUint32>(value & mask);
1262		}
1263		else
1264			color.float32[i] = rnd.getFloat();
1265	}
1266
1267	return color;
1268}
1269
1270} // anonymous
1271
1272tcu::TestCaseGroup* createSamplerBorderSwizzleTests (tcu::TestContext& testCtx, PipelineConstructionType pipelineConstructionType)
1273{
1274	const deUint32 baseSeed = 1610707317u;
1275
1276	const VkFormat textureFormats[] =
1277	{
1278		//VK_FORMAT_UNDEFINED,
1279		VK_FORMAT_R4G4_UNORM_PACK8,
1280		VK_FORMAT_R4G4B4A4_UNORM_PACK16,
1281		VK_FORMAT_B4G4R4A4_UNORM_PACK16,
1282		VK_FORMAT_R5G6B5_UNORM_PACK16,
1283		VK_FORMAT_B5G6R5_UNORM_PACK16,
1284		VK_FORMAT_R5G5B5A1_UNORM_PACK16,
1285		VK_FORMAT_B5G5R5A1_UNORM_PACK16,
1286		VK_FORMAT_A1R5G5B5_UNORM_PACK16,
1287#ifndef CTS_USES_VULKANSC
1288		VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR,
1289#endif // CTS_USES_VULKANSC
1290		VK_FORMAT_R8_UNORM,
1291		VK_FORMAT_R8_SNORM,
1292		//VK_FORMAT_R8_USCALED,
1293		//VK_FORMAT_R8_SSCALED,
1294		VK_FORMAT_R8_UINT,
1295		VK_FORMAT_R8_SINT,
1296		VK_FORMAT_R8_SRGB,
1297#ifndef CTS_USES_VULKANSC
1298		VK_FORMAT_A8_UNORM_KHR,
1299#endif // CTS_USES_VULKANSC
1300		VK_FORMAT_R8G8_UNORM,
1301		VK_FORMAT_R8G8_SNORM,
1302		//VK_FORMAT_R8G8_USCALED,
1303		//VK_FORMAT_R8G8_SSCALED,
1304		VK_FORMAT_R8G8_UINT,
1305		VK_FORMAT_R8G8_SINT,
1306		VK_FORMAT_R8G8_SRGB,
1307		VK_FORMAT_R8G8B8_UNORM,
1308		VK_FORMAT_R8G8B8_SNORM,
1309		//VK_FORMAT_R8G8B8_USCALED,
1310		//VK_FORMAT_R8G8B8_SSCALED,
1311		VK_FORMAT_R8G8B8_UINT,
1312		VK_FORMAT_R8G8B8_SINT,
1313		VK_FORMAT_R8G8B8_SRGB,
1314		VK_FORMAT_B8G8R8_UNORM,
1315		VK_FORMAT_B8G8R8_SNORM,
1316		//VK_FORMAT_B8G8R8_USCALED,
1317		//VK_FORMAT_B8G8R8_SSCALED,
1318		VK_FORMAT_B8G8R8_UINT,
1319		VK_FORMAT_B8G8R8_SINT,
1320		VK_FORMAT_B8G8R8_SRGB,
1321		VK_FORMAT_R8G8B8A8_UNORM,
1322		VK_FORMAT_R8G8B8A8_SNORM,
1323		//VK_FORMAT_R8G8B8A8_USCALED,
1324		//VK_FORMAT_R8G8B8A8_SSCALED,
1325		VK_FORMAT_R8G8B8A8_UINT,
1326		VK_FORMAT_R8G8B8A8_SINT,
1327		VK_FORMAT_R8G8B8A8_SRGB,
1328		VK_FORMAT_B8G8R8A8_UNORM,
1329		VK_FORMAT_B8G8R8A8_SNORM,
1330		//VK_FORMAT_B8G8R8A8_USCALED,
1331		//VK_FORMAT_B8G8R8A8_SSCALED,
1332		VK_FORMAT_B8G8R8A8_UINT,
1333		VK_FORMAT_B8G8R8A8_SINT,
1334		VK_FORMAT_B8G8R8A8_SRGB,
1335		 VK_FORMAT_A8B8G8R8_UNORM_PACK32,
1336		 VK_FORMAT_A8B8G8R8_SNORM_PACK32,
1337		// VK_FORMAT_A8B8G8R8_USCALED_PACK32,
1338		// VK_FORMAT_A8B8G8R8_SSCALED_PACK32,
1339		// VK_FORMAT_A8B8G8R8_UINT_PACK32,
1340		// VK_FORMAT_A8B8G8R8_SINT_PACK32,
1341		// VK_FORMAT_A8B8G8R8_SRGB_PACK32,
1342		VK_FORMAT_A2R10G10B10_UNORM_PACK32,
1343		VK_FORMAT_A2R10G10B10_SNORM_PACK32,
1344		// VK_FORMAT_A2R10G10B10_USCALED_PACK32,
1345		// VK_FORMAT_A2R10G10B10_SSCALED_PACK32,
1346		// VK_FORMAT_A2R10G10B10_UINT_PACK32,
1347		// VK_FORMAT_A2R10G10B10_SINT_PACK32,
1348		VK_FORMAT_A2B10G10R10_UNORM_PACK32,
1349		VK_FORMAT_A2B10G10R10_SNORM_PACK32,
1350		// VK_FORMAT_A2B10G10R10_USCALED_PACK32,
1351		// VK_FORMAT_A2B10G10R10_SSCALED_PACK32,
1352		// VK_FORMAT_A2B10G10R10_UINT_PACK32,
1353		// VK_FORMAT_A2B10G10R10_SINT_PACK32,
1354		VK_FORMAT_R16_UNORM,
1355		VK_FORMAT_R16_SNORM,
1356		//VK_FORMAT_R16_USCALED,
1357		//VK_FORMAT_R16_SSCALED,
1358		VK_FORMAT_R16_UINT,
1359		VK_FORMAT_R16_SINT,
1360		VK_FORMAT_R16_SFLOAT,
1361		VK_FORMAT_R16G16_UNORM,
1362		VK_FORMAT_R16G16_SNORM,
1363		//VK_FORMAT_R16G16_USCALED,
1364		//VK_FORMAT_R16G16_SSCALED,
1365		VK_FORMAT_R16G16_UINT,
1366		VK_FORMAT_R16G16_SINT,
1367		VK_FORMAT_R16G16_SFLOAT,
1368		VK_FORMAT_R16G16B16_UNORM,
1369		VK_FORMAT_R16G16B16_SNORM,
1370		//VK_FORMAT_R16G16B16_USCALED,
1371		//VK_FORMAT_R16G16B16_SSCALED,
1372		VK_FORMAT_R16G16B16_UINT,
1373		VK_FORMAT_R16G16B16_SINT,
1374		VK_FORMAT_R16G16B16_SFLOAT,
1375		VK_FORMAT_R16G16B16A16_UNORM,
1376		VK_FORMAT_R16G16B16A16_SNORM,
1377		//VK_FORMAT_R16G16B16A16_USCALED,
1378		//VK_FORMAT_R16G16B16A16_SSCALED,
1379		VK_FORMAT_R16G16B16A16_UINT,
1380		VK_FORMAT_R16G16B16A16_SINT,
1381		VK_FORMAT_R16G16B16A16_SFLOAT,
1382		VK_FORMAT_R32_UINT,
1383		VK_FORMAT_R32_SINT,
1384		VK_FORMAT_R32_SFLOAT,
1385		VK_FORMAT_R32G32_UINT,
1386		VK_FORMAT_R32G32_SINT,
1387		VK_FORMAT_R32G32_SFLOAT,
1388		VK_FORMAT_R32G32B32_UINT,
1389		VK_FORMAT_R32G32B32_SINT,
1390		VK_FORMAT_R32G32B32_SFLOAT,
1391		VK_FORMAT_R32G32B32A32_UINT,
1392		VK_FORMAT_R32G32B32A32_SINT,
1393		VK_FORMAT_R32G32B32A32_SFLOAT,
1394
1395		// Depth/Stencil formats.
1396		VK_FORMAT_D16_UNORM,
1397		VK_FORMAT_X8_D24_UNORM_PACK32,
1398		VK_FORMAT_D32_SFLOAT,
1399		VK_FORMAT_S8_UINT,
1400		VK_FORMAT_D16_UNORM_S8_UINT,
1401		VK_FORMAT_D24_UNORM_S8_UINT,
1402		VK_FORMAT_D32_SFLOAT_S8_UINT,
1403	};
1404
1405	const std::array<bool, 2> sampleStencilFlag = {{ false, true }};
1406
1407	const auto mappingPermutations = genMappingPermutations();
1408
1409	const struct
1410	{
1411		VkBorderColor	borderType;
1412		const char*		borderTypeName;
1413	}
1414	borderColors[] =
1415	{
1416		{	VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,	"transparent_black"	},
1417		{	VK_BORDER_COLOR_INT_TRANSPARENT_BLACK,		"transparent_black"	},
1418		{	VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK,			"opaque_black"		},
1419		{	VK_BORDER_COLOR_INT_OPAQUE_BLACK,			"opaque_black"		},
1420		{	VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,			"opaque_white"		},
1421		{	VK_BORDER_COLOR_INT_OPAQUE_WHITE,			"opaque_white"		},
1422		{	VK_BORDER_COLOR_FLOAT_CUSTOM_EXT,			"custom"			},
1423		{	VK_BORDER_COLOR_INT_CUSTOM_EXT,				"custom"			},
1424	};
1425
1426	const struct
1427	{
1428		bool		useSwizzleHint;
1429		const char*	name;
1430	} swizzleHintCases[] =
1431	{
1432		{ false,	"no_swizzle_hint"	},
1433		{ true,		"with_swizzle_hint"	},
1434	};
1435
1436	de::MovePtr<tcu::TestCaseGroup> mainGroup(new tcu::TestCaseGroup(testCtx, "border_swizzle"));
1437
1438	for (const auto& format : textureFormats)
1439	{
1440		const auto						skip		= std::strlen("VK_FORMAT_");
1441		const std::string				formatName	= de::toLower(std::string(getFormatName(format)).substr(skip));
1442
1443		for (const auto sampleStencil : sampleStencilFlag)
1444		{
1445			const auto isDSFormat = isDepthStencilFormat(format);
1446
1447			if (!isDSFormat && sampleStencil)
1448				continue;
1449
1450			std::ostringstream formatGroupName;
1451			formatGroupName << formatName;
1452
1453			if (isDSFormat)
1454			{
1455				const auto tcuFormat = mapVkFormat(format);
1456
1457				if (!sampleStencil && !tcu::hasDepthComponent(tcuFormat.order))
1458					continue;
1459				if (sampleStencil && !tcu::hasStencilComponent(tcuFormat.order))
1460					continue;
1461
1462				if (sampleStencil)
1463					formatGroupName << "_stencil";
1464			}
1465
1466			de::MovePtr<tcu::TestCaseGroup>	formatGroup	(new tcu::TestCaseGroup(testCtx, formatGroupName.str().c_str()));
1467
1468			for (size_t mappingIdx = 0u; mappingIdx < mappingPermutations.size(); ++mappingIdx)
1469			{
1470				const auto&						mapping			= mappingPermutations[mappingIdx];
1471				de::MovePtr<tcu::TestCaseGroup>	mappingGroup	(new tcu::TestCaseGroup(testCtx, swizzleArrayToString(mapping).c_str()));
1472
1473				for (int borderColorIdx = 0; borderColorIdx < DE_LENGTH_OF_ARRAY(borderColors); ++borderColorIdx)
1474				{
1475					const auto&						borderColor		= borderColors[borderColorIdx];
1476					de::MovePtr<tcu::TestCaseGroup>	borderTypeGroup	(new tcu::TestCaseGroup(testCtx, borderColor.borderTypeName));
1477
1478					const auto formatType	= getFormatType(format, sampleStencil);
1479					const auto isIntBorder	= isIntegerBorder(borderColor.borderType);
1480
1481					// Skip cases that do not make sense for the format and border type combination.
1482					if (isIntBorder && formatType == FormatType::FLOAT)
1483						continue;
1484					else if (!isIntBorder && formatType != FormatType::FLOAT)
1485						continue;
1486
1487					for (int gatherIdx = -1; gatherIdx <= 3; ++gatherIdx)
1488					{
1489						const auto						componentGather	= gatherIndexToString(gatherIdx);
1490						de::MovePtr<tcu::TestCaseGroup>	gatherGroup		(new tcu::TestCaseGroup(testCtx, componentGather.c_str()));
1491
1492						for (const auto& swizzleHint : swizzleHintCases)
1493						{
1494							TestParams params;
1495							deMemset(&params, 0, sizeof(TestParams));
1496
1497							const deUint32	seed	= baseSeed + static_cast<deUint32>(format) + static_cast<deUint32>(mappingIdx) + static_cast<deUint32>(borderColorIdx) + static_cast<deUint32>(gatherIdx);
1498							de::Random		rnd		(seed);
1499
1500							params.pipelineConstructionType	= pipelineConstructionType;
1501							params.textureFormat			= format;
1502							params.textureColor				= getRandomClearColor(format, rnd, false);
1503							params.textureDepthStencilValue = vk::makeClearDepthStencilValue(0.0f, 0u);
1504
1505							makeComponentMapping(params.componentMapping, mapping);
1506							params.borderColor			= borderColor.borderType;
1507							params.componentGather		= ((gatherIdx < 0) ? tcu::nothing<int>() : tcu::just(gatherIdx));
1508							params.textureCoordinates	= getRandomBorderCoordinates(rnd);
1509
1510							if (params.isCustom())
1511								params.customBorderColor = tcu::just(getRandomClearColor(format, rnd, sampleStencil));
1512							else
1513								params.customBorderColor = tcu::nothing<VkClearColorValue>();
1514
1515							params.useSamplerSwizzleHint = swizzleHint.useSwizzleHint;
1516							params.useStencilAspect		 = sampleStencil;
1517
1518							gatherGroup->addChild(new BorderSwizzleCase(testCtx, swizzleHint.name, params));
1519						}
1520
1521						borderTypeGroup->addChild(gatherGroup.release());
1522					}
1523
1524					mappingGroup->addChild(borderTypeGroup.release());
1525				}
1526
1527				formatGroup->addChild(mappingGroup.release());
1528			}
1529
1530			mainGroup->addChild(formatGroup.release());
1531		}
1532	}
1533
1534	return mainGroup.release();
1535}
1536
1537} // pipeline
1538} // vkt
1539
1540