Home | History | Annotate | Download | only in pipeline
      1 /*------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      3  * ------------------------
      4  *
      5  * Copyright (c) 2016 The Khronos Group Inc.
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Pipeline specialization constants tests
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "vktPipelineSpecConstantTests.hpp"
     25 #include "vktTestCase.hpp"
     26 #include "vktPipelineSpecConstantUtil.hpp"
     27 #include "vktPipelineMakeUtil.hpp"
     28 
     29 #include "tcuTestLog.hpp"
     30 #include "tcuTexture.hpp"
     31 #include "tcuFormatUtil.hpp"
     32 
     33 #include "gluShaderUtil.hpp"
     34 
     35 #include "vkBuilderUtil.hpp"
     36 #include "vkPrograms.hpp"
     37 #include "vkRefUtil.hpp"
     38 #include "vkTypeUtil.hpp"
     39 #include "vkImageUtil.hpp"
     40 
     41 #include "deUniquePtr.hpp"
     42 #include "deStringUtil.hpp"
     43 
     44 namespace vkt
     45 {
     46 namespace pipeline
     47 {
     48 
     49 using namespace vk;
     50 
     51 namespace
     52 {
     53 
     54 static const char* const s_perVertexBlock =	"gl_PerVertex {\n"
     55 											"    vec4 gl_Position;\n"
     56 											"}";
     57 
     58 //! Raw memory storage for values used in test cases.
     59 //! We use it to simplify test case definitions where different types are expected in the result.
     60 class GenericValue
     61 {
     62 public:
     63 	GenericValue (void) { clear(); }
     64 
     65 	//! Copy up to 'size' bytes of 'data'.
     66 	GenericValue (const void* data, const deUint32 size)
     67 	{
     68 		DE_ASSERT(size <= sizeof(m_data));
     69 		clear();
     70 		deMemcpy(&m_data, data, size);
     71 	}
     72 
     73 private:
     74 	deUint64 m_data;
     75 
     76 	void clear (void) { m_data = 0; }
     77 };
     78 
     79 inline GenericValue makeValueBool32	 (const bool a)		{ return GenericValue(&a, sizeof(a)); }
     80 inline GenericValue makeValueInt32	 (const deInt32 a)	{ return GenericValue(&a, sizeof(a)); }
     81 // \note deInt64 not tested
     82 inline GenericValue makeValueUint32	 (const deUint32 a)	{ return GenericValue(&a, sizeof(a)); }
     83 // \note deUint64 not tested
     84 inline GenericValue makeValueFloat32 (const float a)	{ return GenericValue(&a, sizeof(a)); }
     85 inline GenericValue makeValueFloat64 (const double a)	{ return GenericValue(&a, sizeof(a)); }
     86 
     87 struct SpecConstant
     88 {
     89 	deUint32			specID;				//!< specialization constant ID
     90 	std::string			declarationCode;	//!< syntax to declare the constant, use ${ID} as an ID placeholder
     91 	deUint32			size;				//!< data size on the host, 0 = no specialized value
     92 	GenericValue		specValue;			//!< specialized value passed by the API
     93 
     94 	SpecConstant (const deUint32 specID_, const std::string declarationCode_)
     95 		: specID			(specID_)
     96 		, declarationCode	(declarationCode_)
     97 		, size				(0)
     98 		, specValue			()
     99 	{
    100 	}
    101 
    102 	SpecConstant (const deUint32 specID_, const std::string declarationCode_, const deUint32 size_, const GenericValue specValue_)
    103 		: specID			(specID_)
    104 		, declarationCode	(declarationCode_)
    105 		, size				(size_)
    106 		, specValue			(specValue_)
    107 	{
    108 	}
    109 };
    110 
    111 //! Useful when referring to a value in a buffer (i.e. check expected values in SSBO).
    112 struct OffsetValue
    113 {
    114 	deUint32		size;		//!< data size in the buffer (up to sizeof(value))
    115 	deUint32		offset;		//!< offset into the buffer
    116 	GenericValue	value;		//!< value expected to be there
    117 
    118 	OffsetValue (const deUint32 size_, const deUint32 offset_, const GenericValue value_)
    119 		: size		(size_)
    120 		, offset	(offset_)
    121 		, value		(value_)
    122 	{}
    123 };
    124 
    125 //! Get the integer value of 'size' bytes at 'memory' location.
    126 deUint64 memoryAsInteger (const void* memory, const deUint32 size)
    127 {
    128 	DE_ASSERT(size <= sizeof(deUint64));
    129 	deUint64 value = 0;
    130 	deMemcpy(&value, memory, size);
    131 	return value;
    132 }
    133 
    134 inline std::string memoryAsHexString (const void* memory, const deUint32 size)
    135 {
    136 	const deUint8* memoryBytePtr = static_cast<const deUint8*>(memory);
    137 	return de::toString(tcu::formatArray(tcu::Format::HexIterator<deUint8>(memoryBytePtr), tcu::Format::HexIterator<deUint8>(memoryBytePtr + size)));
    138 }
    139 
    140 void logValueMismatch (tcu::TestLog& log, const void* expected, const void* actual, const deUint32 offset, const deUint32 size)
    141 {
    142 	const bool canDisplayValue = (size <= sizeof(deUint64));
    143 	log << tcu::TestLog::Message
    144 		<< "Comparison failed for value at offset " << de::toString(offset) << ": expected "
    145 		<< (canDisplayValue ? de::toString(memoryAsInteger(expected, size)) + " " : "") << memoryAsHexString(expected, size) << " but got "
    146 		<< (canDisplayValue ? de::toString(memoryAsInteger(actual, size)) + " " : "") << memoryAsHexString(actual, size)
    147 		<< tcu::TestLog::EndMessage;
    148 }
    149 
    150 //! Check if expected values exist in the memory.
    151 bool verifyValues (tcu::TestLog& log, const void* memory, const std::vector<OffsetValue>& expectedValues)
    152 {
    153 	bool ok = true;
    154 	log << tcu::TestLog::Section("compare", "Verify result values");
    155 
    156 	for (std::vector<OffsetValue>::const_iterator it = expectedValues.begin(); it < expectedValues.end(); ++it)
    157 	{
    158 		const char* const valuePtr = static_cast<const char*>(memory) + it->offset;
    159 		if (deMemCmp(valuePtr, &it->value, it->size) != 0)
    160 		{
    161 			ok = false;
    162 			logValueMismatch(log, &it->value, valuePtr, it->offset, it->size);
    163 		}
    164 	}
    165 
    166 	if (ok)
    167 		log << tcu::TestLog::Message << "All OK" << tcu::TestLog::EndMessage;
    168 
    169 	log << tcu::TestLog::EndSection;
    170 	return ok;
    171 }
    172 
    173 //! Bundles together common test case parameters.
    174 struct CaseDefinition
    175 {
    176 	std::string					name;				//!< Test case name
    177 	std::vector<SpecConstant>	specConstants;		//!< list of specialization constants to declare
    178 	VkDeviceSize				ssboSize;			//!< required ssbo size in bytes
    179 	std::string					ssboCode;			//!< ssbo member definitions
    180 	std::string					globalCode;			//!< generic shader code outside the main function (e.g. declarations)
    181 	std::string					mainCode;			//!< generic shader code to execute in main (e.g. assignments)
    182 	std::vector<OffsetValue>	expectedValues;		//!< list of values to check inside the ssbo buffer
    183 	FeatureFlags				requirements;		//!< features the implementation must support to allow this test to run
    184 };
    185 
    186 //! Manages Vulkan structures to pass specialization data.
    187 class Specialization
    188 {
    189 public:
    190 											Specialization (const std::vector<SpecConstant>& specConstants);
    191 
    192 	//! Can return NULL if nothing is specialized
    193 	const VkSpecializationInfo*				getSpecializationInfo (void) const { return m_entries.size() > 0 ? &m_specialization : DE_NULL; }
    194 
    195 private:
    196 	std::vector<GenericValue>				m_data;
    197 	std::vector<VkSpecializationMapEntry>	m_entries;
    198 	VkSpecializationInfo					m_specialization;
    199 };
    200 
    201 Specialization::Specialization (const std::vector<SpecConstant>& specConstants)
    202 {
    203 	m_data.reserve(specConstants.size());
    204 	m_entries.reserve(specConstants.size());
    205 
    206 	deUint32 offset = 0;
    207 	for (std::vector<SpecConstant>::const_iterator it = specConstants.begin(); it != specConstants.end(); ++it)
    208 		if (it->size != 0)
    209 		{
    210 			m_data.push_back(it->specValue);
    211 			m_entries.push_back(makeSpecializationMapEntry(it->specID, offset, it->size));
    212 			offset += (deUint32)sizeof(GenericValue);
    213 		}
    214 
    215 	if (m_entries.size() > 0)
    216 	{
    217 		m_specialization.mapEntryCount = static_cast<deUint32>(m_entries.size());
    218 		m_specialization.pMapEntries   = &m_entries[0];
    219 		m_specialization.dataSize	   = sizeof(GenericValue) * m_data.size();
    220 		m_specialization.pData		   = &m_data[0];
    221 	}
    222 	else
    223 		deMemset(&m_specialization, 0, sizeof(m_specialization));
    224 }
    225 
    226 class SpecConstantTest : public TestCase
    227 {
    228 public:
    229 								SpecConstantTest	(tcu::TestContext&				testCtx,
    230 													 const VkShaderStageFlagBits	stage,		//!< which shader stage is tested
    231 													 const CaseDefinition&			caseDef);
    232 
    233 	void						initPrograms		(SourceCollections&		programCollection) const;
    234 	TestInstance*				createInstance		(Context&				context) const;
    235 
    236 private:
    237 	const VkShaderStageFlagBits	m_stage;
    238 	const CaseDefinition		m_caseDef;
    239 };
    240 
    241 SpecConstantTest::SpecConstantTest (tcu::TestContext&			testCtx,
    242 									const VkShaderStageFlagBits	stage,
    243 									const CaseDefinition&		caseDef)
    244 	: TestCase	(testCtx, caseDef.name, "")
    245 	, m_stage	(stage)
    246 	, m_caseDef	(caseDef)
    247 {
    248 }
    249 
    250 //! Build a string that declares all specialization constants, replacing ${ID} with proper ID numbers.
    251 std::string generateSpecConstantCode (const std::vector<SpecConstant>& specConstants)
    252 {
    253 	std::ostringstream code;
    254 	for (std::vector<SpecConstant>::const_iterator it = specConstants.begin(); it != specConstants.end(); ++it)
    255 	{
    256 		std::string decl = it->declarationCode;
    257 		const std::string::size_type pos = decl.find("${ID}");
    258 		if (pos != std::string::npos)
    259 			decl.replace(pos, 5, de::toString(it->specID));
    260 		code << decl << "\n";
    261 	}
    262 	code << "\n";
    263 	return code.str();
    264 }
    265 
    266 std::string generateSSBOCode (const std::string& memberDeclarations)
    267 {
    268 	std::ostringstream code;
    269 	code << "layout (set = 0, binding = 0, std430) writeonly buffer Output {\n"
    270 		 << memberDeclarations
    271 		 << "} sb_out;\n"
    272 		 << "\n";
    273 	return code.str();
    274 }
    275 
    276 void SpecConstantTest::initPrograms (SourceCollections& programCollection) const
    277 {
    278 	// Always add vertex and fragment to graphics stages
    279 	VkShaderStageFlags requiredStages = m_stage;
    280 
    281 	if (requiredStages & VK_SHADER_STAGE_ALL_GRAPHICS)
    282 		requiredStages |= VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
    283 
    284 	if (requiredStages & (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))
    285 		requiredStages |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
    286 
    287 	// Either graphics or compute must be defined, but not both
    288 	DE_ASSERT(((requiredStages & VK_SHADER_STAGE_ALL_GRAPHICS) != 0) != ((requiredStages & VK_SHADER_STAGE_COMPUTE_BIT) != 0));
    289 
    290 	if (requiredStages & VK_SHADER_STAGE_VERTEX_BIT)
    291 	{
    292 		const bool useSpecConst = (m_stage == VK_SHADER_STAGE_VERTEX_BIT);
    293 		std::ostringstream src;
    294 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440) << "\n"
    295 			<< "layout(location = 0) in highp vec4 position;\n"
    296 			<< "\n"
    297 			<< "out " << s_perVertexBlock << ";\n"
    298 			<< "\n"
    299 			<< (useSpecConst ? generateSpecConstantCode(m_caseDef.specConstants) : "")
    300 			<< (useSpecConst ? generateSSBOCode(m_caseDef.ssboCode) : "")
    301 			<< (useSpecConst ? m_caseDef.globalCode + "\n" : "")
    302 			<< "void main (void)\n"
    303 			<< "{\n"
    304 			<< (useSpecConst ? m_caseDef.mainCode + "\n" : "")
    305 			<< "    gl_Position = position;\n"
    306 			<< "}\n";
    307 
    308 		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
    309 	}
    310 
    311 	if (requiredStages & VK_SHADER_STAGE_FRAGMENT_BIT)
    312 	{
    313 		const bool useSpecConst = (m_stage == VK_SHADER_STAGE_FRAGMENT_BIT);
    314 		std::ostringstream src;
    315 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440) << "\n"
    316 			<< "layout(location = 0) out highp vec4 fragColor;\n"
    317 			<< "\n"
    318 			<< (useSpecConst ? generateSpecConstantCode(m_caseDef.specConstants) : "")
    319 			<< (useSpecConst ? generateSSBOCode(m_caseDef.ssboCode) : "")
    320 			<< (useSpecConst ? m_caseDef.globalCode + "\n" : "")
    321 			<< "void main (void)\n"
    322 			<< "{\n"
    323 			<< (useSpecConst ? m_caseDef.mainCode + "\n" : "")
    324 			<< "    fragColor = vec4(1.0, 1.0, 0.0, 1.0);\n"
    325 			<< "}\n";
    326 
    327 		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
    328 	}
    329 
    330 	if (requiredStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
    331 	{
    332 		const bool useSpecConst = (m_stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
    333 		std::ostringstream src;
    334 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440) << "\n"
    335 			<< "layout(vertices = 3) out;\n"
    336 			<< "\n"
    337 			<< "in " << s_perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
    338 			<< "\n"
    339 			<< "out " << s_perVertexBlock << " gl_out[];\n"
    340 			<< "\n"
    341 			<< (useSpecConst ? generateSpecConstantCode(m_caseDef.specConstants) : "")
    342 			<< (useSpecConst ? generateSSBOCode(m_caseDef.ssboCode) : "")
    343 			<< (useSpecConst ? m_caseDef.globalCode + "\n" : "")
    344 			<< "void main (void)\n"
    345 			<< "{\n"
    346 			<< (useSpecConst ? m_caseDef.mainCode + "\n" : "")
    347 			<< "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
    348 			<< "    if (gl_InvocationID == 0)\n"
    349 			<< "    {\n"
    350 			<< "        gl_TessLevelInner[0] = 3;\n"
    351 			<< "        gl_TessLevelOuter[0] = 2;\n"
    352 			<< "        gl_TessLevelOuter[1] = 2;\n"
    353 			<< "        gl_TessLevelOuter[2] = 2;\n"
    354 			<< "    }\n"
    355 			<< "}\n";
    356 
    357 		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
    358 	}
    359 
    360 	if (requiredStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
    361 	{
    362 		const bool useSpecConst = (m_stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
    363 		std::ostringstream src;
    364 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440) << "\n"
    365 			<< "layout(triangles, equal_spacing, ccw) in;\n"
    366 			<< "\n"
    367 			<< "in " << s_perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
    368 			<< "\n"
    369 			<< "out " << s_perVertexBlock << ";\n"
    370 			<< "\n"
    371 			<< (useSpecConst ? generateSpecConstantCode(m_caseDef.specConstants) : "")
    372 			<< (useSpecConst ? generateSSBOCode(m_caseDef.ssboCode) : "")
    373 			<< (useSpecConst ? m_caseDef.globalCode + "\n" : "")
    374 			<< "void main (void)\n"
    375 			<< "{\n"
    376 			<< (useSpecConst ? m_caseDef.mainCode + "\n" : "")
    377 			<< "    vec3 p0 = gl_TessCoord.x * gl_in[0].gl_Position.xyz;\n"
    378 			<< "    vec3 p1 = gl_TessCoord.y * gl_in[1].gl_Position.xyz;\n"
    379 			<< "    vec3 p2 = gl_TessCoord.z * gl_in[2].gl_Position.xyz;\n"
    380 			<< "    gl_Position = vec4(p0 + p1 + p2, 1.0);\n"
    381 			<< "}\n";
    382 
    383 		programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
    384 	}
    385 
    386 	if (requiredStages & VK_SHADER_STAGE_GEOMETRY_BIT)
    387 	{
    388 		const bool useSpecConst = (m_stage == VK_SHADER_STAGE_GEOMETRY_BIT);
    389 		std::ostringstream src;
    390 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440) << "\n"
    391 			<< "layout(triangles) in;\n"
    392 			<< "layout(triangle_strip, max_vertices = 3) out;\n"
    393 			<< "\n"
    394 			<< "in " << s_perVertexBlock << " gl_in[];\n"
    395 			<< "\n"
    396 			<< "out " << s_perVertexBlock << ";\n"
    397 			<< "\n"
    398 			<< (useSpecConst ? generateSpecConstantCode(m_caseDef.specConstants) : "")
    399 			<< (useSpecConst ? generateSSBOCode(m_caseDef.ssboCode) : "")
    400 			<< (useSpecConst ? m_caseDef.globalCode + "\n" : "")
    401 			<< "void main (void)\n"
    402 			<< "{\n"
    403 			<< (useSpecConst ? m_caseDef.mainCode + "\n" : "")
    404 			<< "    gl_Position = gl_in[0].gl_Position;\n"
    405 			<< "    EmitVertex();\n"
    406 			<< "\n"
    407 			<< "    gl_Position = gl_in[1].gl_Position;\n"
    408 			<< "    EmitVertex();\n"
    409 			<< "\n"
    410 			<< "    gl_Position = gl_in[2].gl_Position;\n"
    411 			<< "    EmitVertex();\n"
    412 			<< "\n"
    413 			<< "    EndPrimitive();\n"
    414 			<< "}\n";
    415 
    416 		programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
    417 	}
    418 
    419 	if (requiredStages & VK_SHADER_STAGE_COMPUTE_BIT)
    420 	{
    421 		std::ostringstream src;
    422 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440) << "\n"
    423 			// Don't define work group size, use the default or specialization constants
    424 			<< "\n"
    425 			<< generateSpecConstantCode(m_caseDef.specConstants)
    426 			<< generateSSBOCode(m_caseDef.ssboCode)
    427 			<< m_caseDef.globalCode + "\n"
    428 			<< "void main (void)\n"
    429 			<< "{\n"
    430 			<< m_caseDef.mainCode
    431 			<< "}\n";
    432 
    433 		programCollection.glslSources.add("comp") << glu::ComputeSource(src.str());
    434 	}
    435 }
    436 
    437 class ComputeTestInstance : public TestInstance
    438 {
    439 public:
    440 									ComputeTestInstance	(Context&							context,
    441 														 const VkDeviceSize					ssboSize,
    442 														 const std::vector<SpecConstant>&	specConstants,
    443 														 const std::vector<OffsetValue>&	expectedValues);
    444 
    445 	tcu::TestStatus					iterate				(void);
    446 
    447 private:
    448 	const VkDeviceSize				m_ssboSize;
    449 	const std::vector<SpecConstant>	m_specConstants;
    450 	const std::vector<OffsetValue>	m_expectedValues;
    451 };
    452 
    453 ComputeTestInstance::ComputeTestInstance (Context&							context,
    454 										  const VkDeviceSize				ssboSize,
    455 										  const std::vector<SpecConstant>&	specConstants,
    456 										  const std::vector<OffsetValue>&	expectedValues)
    457 	: TestInstance		(context)
    458 	, m_ssboSize		(ssboSize)
    459 	, m_specConstants	(specConstants)
    460 	, m_expectedValues	(expectedValues)
    461 {
    462 }
    463 
    464 tcu::TestStatus ComputeTestInstance::iterate (void)
    465 {
    466 	const DeviceInterface&	vk					= m_context.getDeviceInterface();
    467 	const VkDevice			device				= m_context.getDevice();
    468 	const VkQueue			queue				= m_context.getUniversalQueue();
    469 	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
    470 	Allocator&				allocator			= m_context.getDefaultAllocator();
    471 
    472 	// Descriptors
    473 
    474 	const Buffer resultBuffer(vk, device, allocator, makeBufferCreateInfo(m_ssboSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
    475 
    476 	const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
    477 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT)
    478 		.build(vk, device));
    479 
    480 	const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
    481 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
    482 		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
    483 
    484 	const Unique<VkDescriptorSet> descriptorSet        (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
    485 	const VkDescriptorBufferInfo  descriptorBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, m_ssboSize);
    486 
    487 	DescriptorSetUpdateBuilder()
    488 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorBufferInfo)
    489 		.update(vk, device);
    490 
    491 	// Specialization
    492 
    493 	const Specialization        specialization (m_specConstants);
    494 	const VkSpecializationInfo* pSpecInfo      = specialization.getSpecializationInfo();
    495 
    496 	// Pipeline
    497 
    498 	const Unique<VkShaderModule>   shaderModule  (createShaderModule (vk, device, m_context.getBinaryCollection().get("comp"), 0));
    499 	const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout (vk, device, *descriptorSetLayout));
    500 	const Unique<VkPipeline>       pipeline      (makeComputePipeline(vk, device, *pipelineLayout, *shaderModule, pSpecInfo));
    501 	const Unique<VkCommandPool>    cmdPool       (createCommandPool  (vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
    502 	const Unique<VkCommandBuffer>  cmdBuffer     (makeCommandBuffer  (vk, device, *cmdPool));
    503 
    504 	beginCommandBuffer(vk, *cmdBuffer);
    505 
    506 	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
    507 	vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
    508 
    509 	vk.cmdDispatch(*cmdBuffer, 1u, 1u, 1u);
    510 
    511 	{
    512 		const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
    513 			VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, m_ssboSize);
    514 
    515 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
    516 			0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
    517 	}
    518 
    519 	VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
    520 	submitCommandsAndWait(vk, device, queue, *cmdBuffer);
    521 
    522 	// Verify results
    523 
    524 	const Allocation& resultAlloc = resultBuffer.getAllocation();
    525 	invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), m_ssboSize);
    526 
    527 	if (verifyValues(m_context.getTestContext().getLog(), resultAlloc.getHostPtr(), m_expectedValues))
    528 		return tcu::TestStatus::pass("Success");
    529 	else
    530 		return tcu::TestStatus::fail("Values did not match");
    531 }
    532 
    533 class GraphicsTestInstance : public TestInstance
    534 {
    535 public:
    536 									GraphicsTestInstance (Context&							context,
    537 														  const VkDeviceSize				ssboSize,
    538 														  const std::vector<SpecConstant>&	specConstants,
    539 														  const std::vector<OffsetValue>&	expectedValues,
    540 														  const VkShaderStageFlagBits		stage);
    541 
    542 	tcu::TestStatus					iterate				 (void);
    543 
    544 private:
    545 	const VkDeviceSize				m_ssboSize;
    546 	const std::vector<SpecConstant>	m_specConstants;
    547 	const std::vector<OffsetValue>	m_expectedValues;
    548 	const VkShaderStageFlagBits		m_stage;
    549 };
    550 
    551 GraphicsTestInstance::GraphicsTestInstance (Context&							context,
    552 											const VkDeviceSize					ssboSize,
    553 											const std::vector<SpecConstant>&	specConstants,
    554 											const std::vector<OffsetValue>&		expectedValues,
    555 											const VkShaderStageFlagBits			stage)
    556 	: TestInstance		(context)
    557 	, m_ssboSize		(ssboSize)
    558 	, m_specConstants	(specConstants)
    559 	, m_expectedValues	(expectedValues)
    560 	, m_stage			(stage)
    561 {
    562 }
    563 
    564 tcu::TestStatus GraphicsTestInstance::iterate (void)
    565 {
    566 	const DeviceInterface&	vk					= m_context.getDeviceInterface();
    567 	const VkDevice			device				= m_context.getDevice();
    568 	const VkQueue			queue				= m_context.getUniversalQueue();
    569 	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
    570 	Allocator&				allocator			= m_context.getDefaultAllocator();
    571 
    572 	// Color attachment
    573 
    574 	const tcu::IVec2          renderSize    = tcu::IVec2(32, 32);
    575 	const VkFormat            imageFormat   = VK_FORMAT_R8G8B8A8_UNORM;
    576 	const Image               colorImage    (vk, device, allocator, makeImageCreateInfo(renderSize, imageFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), MemoryRequirement::Any);
    577 	const Unique<VkImageView> colorImageView(makeImageView(vk, device, *colorImage, VK_IMAGE_VIEW_TYPE_2D, imageFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u)));
    578 
    579 	// Vertex buffer
    580 
    581 	const deUint32     numVertices           = 3;
    582 	const VkDeviceSize vertexBufferSizeBytes = sizeof(tcu::Vec4) * numVertices;
    583 	const Buffer       vertexBuffer          (vk, device, allocator, makeBufferCreateInfo(vertexBufferSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
    584 
    585 	{
    586 		const Allocation& alloc = vertexBuffer.getAllocation();
    587 		tcu::Vec4* pVertices = reinterpret_cast<tcu::Vec4*>(alloc.getHostPtr());
    588 
    589 		pVertices[0] = tcu::Vec4(-1.0f, -1.0f,  0.0f,  1.0f);
    590 		pVertices[1] = tcu::Vec4(-1.0f,  1.0f,  0.0f,  1.0f);
    591 		pVertices[2] = tcu::Vec4( 1.0f, -1.0f,  0.0f,  1.0f);
    592 
    593 		flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexBufferSizeBytes);
    594 		// No barrier needed, flushed memory is automatically visible
    595 	}
    596 
    597 	// Descriptors
    598 
    599 	const Buffer resultBuffer(vk, device, allocator, makeBufferCreateInfo(m_ssboSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
    600 
    601 	const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
    602 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_ALL_GRAPHICS)
    603 		.build(vk, device));
    604 
    605 	const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
    606 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
    607 		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
    608 
    609 	const Unique<VkDescriptorSet> descriptorSet        (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
    610 	const VkDescriptorBufferInfo  descriptorBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, m_ssboSize);
    611 
    612 	DescriptorSetUpdateBuilder()
    613 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorBufferInfo)
    614 		.update(vk, device);
    615 
    616 	// Specialization
    617 
    618 	const Specialization        specialization (m_specConstants);
    619 	const VkSpecializationInfo* pSpecInfo      = specialization.getSpecializationInfo();
    620 
    621 	// Pipeline
    622 
    623 	const Unique<VkRenderPass>     renderPass    (makeRenderPass    (vk, device, imageFormat));
    624 	const Unique<VkFramebuffer>    framebuffer   (makeFramebuffer	(vk, device, *renderPass, 1u, &colorImageView.get(), static_cast<deUint32>(renderSize.x()), static_cast<deUint32>(renderSize.y())));
    625 	const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
    626 	const Unique<VkCommandPool>    cmdPool       (createCommandPool (vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
    627 	const Unique<VkCommandBuffer>  cmdBuffer     (makeCommandBuffer (vk, device, *cmdPool));
    628 
    629 	GraphicsPipelineBuilder pipelineBuilder;
    630 	pipelineBuilder
    631 		.setRenderSize(renderSize)
    632 		.setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT,   m_context.getBinaryCollection().get("vert"), pSpecInfo)
    633 		.setShader(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), pSpecInfo);
    634 
    635 	if ((m_stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) || (m_stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT))
    636 		pipelineBuilder
    637 			.setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), pSpecInfo)
    638 			.setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), pSpecInfo);
    639 
    640 	if (m_stage == VK_SHADER_STAGE_GEOMETRY_BIT)
    641 		pipelineBuilder
    642 			.setShader(vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get("geom"), pSpecInfo);
    643 
    644 	const Unique<VkPipeline> pipeline (pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass));
    645 
    646 	// Draw commands
    647 
    648 	const VkRect2D renderArea = {
    649 		makeOffset2D(0, 0),
    650 		makeExtent2D(renderSize.x(), renderSize.y()),
    651 	};
    652 	const tcu::Vec4    clearColor         (0.0f, 0.0f, 0.0f, 1.0f);
    653 	const VkDeviceSize vertexBufferOffset = 0ull;
    654 
    655 	beginCommandBuffer(vk, *cmdBuffer);
    656 
    657 	{
    658 		const VkImageSubresourceRange imageFullSubresourceRange              = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
    659 		const VkImageMemoryBarrier    barrierColorAttachmentSetInitialLayout = makeImageMemoryBarrier(
    660 			0u, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    661 			VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    662 			*colorImage, imageFullSubresourceRange);
    663 
    664 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u,
    665 			0u, DE_NULL, 0u, DE_NULL, 1u, &barrierColorAttachmentSetInitialLayout);
    666 	}
    667 
    668 	beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
    669 
    670 	vk.cmdBindPipeline      (*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
    671 	vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
    672 	vk.cmdBindVertexBuffers (*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
    673 
    674 	vk.cmdDraw(*cmdBuffer, numVertices, 1u, 0u, 0u);
    675 	vk.cmdEndRenderPass(*cmdBuffer);
    676 
    677 	{
    678 		const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
    679 			VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, m_ssboSize);
    680 
    681 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
    682 			0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
    683 	}
    684 
    685 	VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
    686 	submitCommandsAndWait(vk, device, queue, *cmdBuffer);
    687 
    688 	// Verify results
    689 
    690 	const Allocation& resultAlloc = resultBuffer.getAllocation();
    691 	invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), m_ssboSize);
    692 
    693 	if (verifyValues(m_context.getTestContext().getLog(), resultAlloc.getHostPtr(), m_expectedValues))
    694 		return tcu::TestStatus::pass("Success");
    695 	else
    696 		return tcu::TestStatus::fail("Values did not match");
    697 }
    698 
    699 FeatureFlags getShaderStageRequirements (const VkShaderStageFlags stageFlags)
    700 {
    701 	FeatureFlags features = (FeatureFlags)0;
    702 
    703 	if (((stageFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) != 0) || ((stageFlags & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) != 0))
    704 		features |= FEATURE_TESSELLATION_SHADER;
    705 
    706 	if ((stageFlags & VK_SHADER_STAGE_GEOMETRY_BIT) != 0)
    707 		features |= FEATURE_GEOMETRY_SHADER;
    708 
    709 	// All tests use SSBO writes to read back results.
    710 	if ((stageFlags & VK_SHADER_STAGE_ALL_GRAPHICS) != 0)
    711 	{
    712 		if ((stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) != 0)
    713 			features |= FEATURE_FRAGMENT_STORES_AND_ATOMICS;
    714 		else
    715 			features |= FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS;
    716 	}
    717 
    718 	return features;
    719 }
    720 
    721 TestInstance* SpecConstantTest::createInstance (Context& context) const
    722 {
    723 	requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), m_caseDef.requirements | getShaderStageRequirements(m_stage));
    724 
    725 	if (m_stage & VK_SHADER_STAGE_COMPUTE_BIT)
    726 		return new ComputeTestInstance(context, m_caseDef.ssboSize, m_caseDef.specConstants, m_caseDef.expectedValues);
    727 	else
    728 		return new GraphicsTestInstance(context, m_caseDef.ssboSize, m_caseDef.specConstants, m_caseDef.expectedValues, m_stage);
    729 }
    730 
    731 //! Declare specialization constants but use them with default values.
    732 tcu::TestCaseGroup* createDefaultValueTests (tcu::TestContext& testCtx, const VkShaderStageFlagBits shaderStage)
    733 {
    734 	de::MovePtr<tcu::TestCaseGroup> testGroup (new tcu::TestCaseGroup(testCtx, "default_value", "use default constant value"));
    735 
    736 	const CaseDefinition defs[] =
    737 	{
    738 		{
    739 			"bool",
    740 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const bool sc0 = true;"),
    741 					   SpecConstant(2u, "layout(constant_id = ${ID}) const bool sc1 = false;")),
    742 			8,
    743 			"    bool r0;\n"
    744 			"    bool r1;\n",
    745 			"",
    746 			"    sb_out.r0 = sc0;\n"
    747 			"    sb_out.r1 = sc1;\n",
    748 			makeVector(OffsetValue(4, 0, makeValueBool32(true)),
    749 					   OffsetValue(4, 4, makeValueBool32(false))),
    750 			(FeatureFlags)0,
    751 		},
    752 		{
    753 			"int",
    754 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = -3;"),
    755 					   SpecConstant(2u, "layout(constant_id = ${ID}) const int sc1 = 17;")),
    756 			8,
    757 			"    int r0;\n"
    758 			"    int r1;\n",
    759 			"",
    760 			"    sb_out.r0 = sc0;\n"
    761 			"    sb_out.r1 = sc1;\n",
    762 			makeVector(OffsetValue(4, 0, makeValueInt32(-3)),
    763 					   OffsetValue(4, 4, makeValueInt32(17))),
    764 			(FeatureFlags)0,
    765 		},
    766 		{
    767 			"uint",
    768 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const uint sc0 = 42u;")),
    769 			4,
    770 			"    uint r0;\n",
    771 			"",
    772 			"    sb_out.r0 = sc0;\n",
    773 			makeVector(OffsetValue(4, 0, makeValueUint32(42u))),
    774 			(FeatureFlags)0,
    775 		},
    776 		{
    777 			"float",
    778 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const float sc0 = 7.5;")),
    779 			4,
    780 			"    float r0;\n",
    781 			"",
    782 			"    sb_out.r0 = sc0;\n",
    783 			makeVector(OffsetValue(4, 0, makeValueFloat32(7.5f))),
    784 			(FeatureFlags)0,
    785 		},
    786 		{
    787 			"double",
    788 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const double sc0 = 2.75LF;")),
    789 			8,
    790 			"    double r0;\n",
    791 			"",
    792 			"    sb_out.r0 = sc0;\n",
    793 			makeVector(OffsetValue(8, 0, makeValueFloat64(2.75))),
    794 			FEATURE_SHADER_FLOAT_64,
    795 		},
    796 	};
    797 
    798 	for (int defNdx = 0; defNdx < DE_LENGTH_OF_ARRAY(defs); ++defNdx)
    799 		testGroup->addChild(new SpecConstantTest(testCtx, shaderStage, defs[defNdx]));
    800 
    801 	return testGroup.release();
    802 }
    803 
    804 //! Declare specialization constants and specify their values through API.
    805 tcu::TestCaseGroup* createBasicSpecializationTests (tcu::TestContext& testCtx, const VkShaderStageFlagBits shaderStage)
    806 {
    807 	de::MovePtr<tcu::TestCaseGroup> testGroup (new tcu::TestCaseGroup(testCtx, "basic", "specialize a constant"));
    808 
    809 	const CaseDefinition defs[] =
    810 	{
    811 		{
    812 			"bool",
    813 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const bool sc0 = true;",  4, makeValueBool32(true)),
    814 					   SpecConstant(2u, "layout(constant_id = ${ID}) const bool sc1 = false;", 4, makeValueBool32(false)),
    815 					   SpecConstant(3u, "layout(constant_id = ${ID}) const bool sc2 = true;",  4, makeValueBool32(false)),
    816 					   SpecConstant(4u, "layout(constant_id = ${ID}) const bool sc3 = false;", 4, makeValueBool32(true))),
    817 			16,
    818 			"    bool r0;\n"
    819 			"    bool r1;\n"
    820 			"    bool r2;\n"
    821 			"    bool r3;\n",
    822 			"",
    823 			"    sb_out.r0 = sc0;\n"
    824 			"    sb_out.r1 = sc1;\n"
    825 			"    sb_out.r2 = sc2;\n"
    826 			"    sb_out.r3 = sc3;\n",
    827 			makeVector(OffsetValue(4,  0, makeValueBool32(true)),
    828 					   OffsetValue(4,  4, makeValueBool32(false)),
    829 					   OffsetValue(4,  8, makeValueBool32(false)),
    830 					   OffsetValue(4, 12, makeValueBool32(true))),
    831 			(FeatureFlags)0,
    832 		},
    833 		{
    834 			"int",
    835 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = -3;", 4, makeValueInt32(33)),
    836 					   SpecConstant(2u, "layout(constant_id = ${ID}) const int sc1 = 91;"),
    837 					   SpecConstant(3u, "layout(constant_id = ${ID}) const int sc2 = 17;", 4, makeValueInt32(-15))),
    838 			12,
    839 			"    int r0;\n"
    840 			"    int r1;\n"
    841 			"    int r2;\n",
    842 			"",
    843 			"    sb_out.r0 = sc0;\n"
    844 			"    sb_out.r1 = sc1;\n"
    845 			"    sb_out.r2 = sc2;\n",
    846 			makeVector(OffsetValue(4, 0, makeValueInt32(33)),
    847 					   OffsetValue(4, 4, makeValueInt32(91)),
    848 					   OffsetValue(4, 8, makeValueInt32(-15))),
    849 			(FeatureFlags)0,
    850 		},
    851 		{
    852 			"uint",
    853 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const uint sc0 = 42u;", 4, makeValueUint32(97u)),
    854 					   SpecConstant(2u, "layout(constant_id = ${ID}) const uint sc1 = 7u;")),
    855 			8,
    856 			"    uint r0;\n"
    857 			"    uint r1;\n",
    858 			"",
    859 			"    sb_out.r0 = sc0;\n"
    860 			"    sb_out.r1 = sc1;\n",
    861 			makeVector(OffsetValue(4, 0, makeValueUint32(97u)),
    862 					   OffsetValue(4, 4, makeValueUint32(7u))),
    863 			(FeatureFlags)0,
    864 		},
    865 		{
    866 			"float",
    867 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const float sc0 = 7.5;", 4, makeValueFloat32(15.75f)),
    868 					   SpecConstant(2u, "layout(constant_id = ${ID}) const float sc1 = 1.125;")),
    869 			8,
    870 			"    float r0;\n"
    871 			"    float r1;\n",
    872 			"",
    873 			"    sb_out.r0 = sc0;\n"
    874 			"    sb_out.r1 = sc1;\n",
    875 			makeVector(OffsetValue(4, 0, makeValueFloat32(15.75f)),
    876 					   OffsetValue(4, 4, makeValueFloat32(1.125f))),
    877 			(FeatureFlags)0,
    878 		},
    879 		{
    880 			"double",
    881 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const double sc0 = 2.75LF;", 8, makeValueFloat64(22.5)),
    882 					   SpecConstant(2u, "layout(constant_id = ${ID}) const double sc1 = 9.25LF;")),
    883 			16,
    884 			"    double r0;\n"
    885 			"    double r1;\n",
    886 			"",
    887 			"    sb_out.r0 = sc0;\n"
    888 			"    sb_out.r1 = sc1;\n",
    889 			makeVector(OffsetValue(8, 0, makeValueFloat64(22.5)),
    890 					   OffsetValue(8, 8, makeValueFloat64(9.25))),
    891 			FEATURE_SHADER_FLOAT_64,
    892 		},
    893 	};
    894 
    895 	for (int defNdx = 0; defNdx < DE_LENGTH_OF_ARRAY(defs); ++defNdx)
    896 		testGroup->addChild(new SpecConstantTest(testCtx, shaderStage, defs[defNdx]));
    897 
    898 	return testGroup.release();
    899 }
    900 
    901 //! Specify compute shader work group size through specialization constants.
    902 tcu::TestCaseGroup* createWorkGroupSizeTests (tcu::TestContext& testCtx)
    903 {
    904 	de::MovePtr<tcu::TestCaseGroup> testGroup (new tcu::TestCaseGroup(testCtx, "local_size", "work group size specialization"));
    905 
    906 	const deUint32 ssboSize = 16;
    907 	const std::string ssboDecl =
    908 		"    uvec3 workGroupSize;\n"
    909 		"    uint  checksum;\n";
    910 	const std::string globalDecl = "shared uint count;\n";
    911 	const std::string mainCode =
    912 		"    count = 0u;\n"
    913 		"\n"
    914 		"    groupMemoryBarrier();\n"
    915 		"    barrier();\n"
    916 		"\n"
    917 		"    atomicAdd(count, 1u);\n"
    918 		"\n"
    919 		"    groupMemoryBarrier();\n"
    920 		"    barrier();\n"
    921 		"\n"
    922 		"    sb_out.workGroupSize = gl_WorkGroupSize;\n"
    923 		"    sb_out.checksum      = count;\n";
    924 
    925 	const CaseDefinition defs[] =
    926 	{
    927 		{
    928 			"x",
    929 			makeVector(SpecConstant(1u, "layout(local_size_x_id = ${ID}) in;",  4, makeValueUint32(7u))),
    930 			ssboSize, ssboDecl, globalDecl, mainCode,
    931 			makeVector(OffsetValue(4,  0, makeValueUint32(7u)),
    932 					   OffsetValue(4,  4, makeValueUint32(1u)),
    933 					   OffsetValue(4,  8, makeValueUint32(1u)),
    934 					   OffsetValue(4, 12, makeValueUint32(7u))),
    935 			(FeatureFlags)0,
    936 		},
    937 		{
    938 			"y",
    939 			makeVector(SpecConstant(1u, "layout(local_size_y_id = ${ID}) in;",  4, makeValueUint32(5u))),
    940 			ssboSize, ssboDecl, globalDecl, mainCode,
    941 			makeVector(OffsetValue(4,  0, makeValueUint32(1u)),
    942 					   OffsetValue(4,  4, makeValueUint32(5u)),
    943 					   OffsetValue(4,  8, makeValueUint32(1u)),
    944 					   OffsetValue(4, 12, makeValueUint32(5u))),
    945 			(FeatureFlags)0,
    946 		},
    947 		{
    948 			"z",
    949 			makeVector(SpecConstant(1u, "layout(local_size_z_id = ${ID}) in;",  4, makeValueUint32(3u))),
    950 			ssboSize, ssboDecl, globalDecl, mainCode,
    951 			makeVector(OffsetValue(4,  0, makeValueUint32(1u)),
    952 					   OffsetValue(4,  4, makeValueUint32(1u)),
    953 					   OffsetValue(4,  8, makeValueUint32(3u)),
    954 					   OffsetValue(4, 12, makeValueUint32(3u))),
    955 			(FeatureFlags)0,
    956 		},
    957 		{
    958 			"xy",
    959 			makeVector(SpecConstant(1u, "layout(local_size_x_id = ${ID}) in;",  4, makeValueUint32(6u)),
    960 					   SpecConstant(2u, "layout(local_size_y_id = ${ID}) in;",  4, makeValueUint32(4u))),
    961 			ssboSize, ssboDecl, globalDecl, mainCode,
    962 			makeVector(OffsetValue(4,  0, makeValueUint32(6u)),
    963 					   OffsetValue(4,  4, makeValueUint32(4u)),
    964 					   OffsetValue(4,  8, makeValueUint32(1u)),
    965 					   OffsetValue(4, 12, makeValueUint32(6u * 4u))),
    966 			(FeatureFlags)0,
    967 		},
    968 		{
    969 			"xz",
    970 			makeVector(SpecConstant(1u, "layout(local_size_x_id = ${ID}) in;",  4, makeValueUint32(3u)),
    971 					   SpecConstant(2u, "layout(local_size_z_id = ${ID}) in;",  4, makeValueUint32(9u))),
    972 			ssboSize, ssboDecl, globalDecl, mainCode,
    973 			makeVector(OffsetValue(4,  0, makeValueUint32(3u)),
    974 					   OffsetValue(4,  4, makeValueUint32(1u)),
    975 					   OffsetValue(4,  8, makeValueUint32(9u)),
    976 					   OffsetValue(4, 12, makeValueUint32(3u * 9u))),
    977 			(FeatureFlags)0,
    978 		},
    979 		{
    980 			"yz",
    981 			makeVector(SpecConstant(1u, "layout(local_size_y_id = ${ID}) in;",  4, makeValueUint32(2u)),
    982 					   SpecConstant(2u, "layout(local_size_z_id = ${ID}) in;",  4, makeValueUint32(5u))),
    983 			ssboSize, ssboDecl, globalDecl, mainCode,
    984 			makeVector(OffsetValue(4,  0, makeValueUint32(1u)),
    985 					   OffsetValue(4,  4, makeValueUint32(2u)),
    986 					   OffsetValue(4,  8, makeValueUint32(5u)),
    987 					   OffsetValue(4, 12, makeValueUint32(2u * 5u))),
    988 			(FeatureFlags)0,
    989 		},
    990 		{
    991 			"xyz",
    992 			makeVector(SpecConstant(1u, "layout(local_size_x_id = ${ID}) in;",  4, makeValueUint32(3u)),
    993 					   SpecConstant(2u, "layout(local_size_y_id = ${ID}) in;",  4, makeValueUint32(5u)),
    994 					   SpecConstant(3u, "layout(local_size_z_id = ${ID}) in;",  4, makeValueUint32(7u))),
    995 			ssboSize, ssboDecl, globalDecl, mainCode,
    996 			makeVector(OffsetValue(4,  0, makeValueUint32(3u)),
    997 					   OffsetValue(4,  4, makeValueUint32(5u)),
    998 					   OffsetValue(4,  8, makeValueUint32(7u)),
    999 					   OffsetValue(4, 12, makeValueUint32(3u * 5u * 7u))),
   1000 			(FeatureFlags)0,
   1001 		},
   1002 	};
   1003 
   1004 	for (int defNdx = 0; defNdx < DE_LENGTH_OF_ARRAY(defs); ++defNdx)
   1005 		testGroup->addChild(new SpecConstantTest(testCtx, VK_SHADER_STAGE_COMPUTE_BIT, defs[defNdx]));
   1006 
   1007 	return testGroup.release();
   1008 }
   1009 
   1010 //! Override a built-in variable with specialization constant value.
   1011 tcu::TestCaseGroup* createBuiltInOverrideTests (tcu::TestContext& testCtx, const VkShaderStageFlagBits shaderStage)
   1012 {
   1013 	de::MovePtr<tcu::TestCaseGroup> testGroup (new tcu::TestCaseGroup(testCtx, "builtin", "built-in override"));
   1014 
   1015 	const CaseDefinition defs[] =
   1016 	{
   1017 		{
   1018 			"default",
   1019 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) gl_MaxImageUnits;")),
   1020 			4,
   1021 			"    bool ok;\n",
   1022 			"",
   1023 			"    sb_out.ok = (gl_MaxImageUnits >= 8);\n",	// implementation defined, 8 is the minimum
   1024 			makeVector(OffsetValue(4,  0, makeValueBool32(true))),
   1025 			(FeatureFlags)0,
   1026 		},
   1027 		{
   1028 			"specialized",
   1029 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) gl_MaxImageUnits;", 4, makeValueInt32(12))),
   1030 			4,
   1031 			"    int maxImageUnits;\n",
   1032 			"",
   1033 			"    sb_out.maxImageUnits = gl_MaxImageUnits;\n",
   1034 			makeVector(OffsetValue(4,  0, makeValueInt32(12))),
   1035 			(FeatureFlags)0,
   1036 		},
   1037 	};
   1038 
   1039 	for (int defNdx = 0; defNdx < DE_LENGTH_OF_ARRAY(defs); ++defNdx)
   1040 		testGroup->addChild(new SpecConstantTest(testCtx, shaderStage, defs[defNdx]));
   1041 
   1042 	return testGroup.release();
   1043 }
   1044 
   1045 //! Specialization constants used in expressions.
   1046 tcu::TestCaseGroup* createExpressionTests (tcu::TestContext& testCtx, const VkShaderStageFlagBits shaderStage)
   1047 {
   1048 	de::MovePtr<tcu::TestCaseGroup> testGroup (new tcu::TestCaseGroup(testCtx, "expression", "specialization constants usage in expressions"));
   1049 
   1050 	const CaseDefinition defs[] =
   1051 	{
   1052 		{
   1053 			"spec_const_expression",
   1054 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = 2;"),
   1055 					   SpecConstant(2u, "layout(constant_id = ${ID}) const int sc1 = 3;", 4, makeValueInt32(5))),
   1056 			4,
   1057 			"    int result;\n",
   1058 
   1059 			"const int expr0 = sc0 + 1;\n"
   1060 			"const int expr1 = sc0 + sc1;\n",
   1061 
   1062 			"    sb_out.result = expr0 + expr1;\n",
   1063 			makeVector(OffsetValue(4,  0, makeValueInt32(10))),
   1064 			(FeatureFlags)0,
   1065 		},
   1066 		{
   1067 			"array_size",
   1068 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = 1;"),
   1069 					   SpecConstant(2u, "layout(constant_id = ${ID}) const int sc1 = 2;", 4, makeValueInt32(3))),
   1070 			16,
   1071 			"    int r0;\n"
   1072 			"    int r1[3];\n",
   1073 
   1074 			"",
   1075 
   1076 			"    int a0[sc0];\n"
   1077 			"    int a1[sc1];\n"
   1078 			"\n"
   1079 			"    for (int i = 0; i < sc0; ++i)\n"
   1080 			"        a0[i] = sc0 - i;\n"
   1081 			"    for (int i = 0; i < sc1; ++i)\n"
   1082 			"        a1[i] = sc1 - i;\n"
   1083 			"\n"
   1084 			"    sb_out.r0 = a0[0];\n"
   1085 			"    for (int i = 0; i < sc1; ++i)\n"
   1086 			"	     sb_out.r1[i] = a1[i];\n",
   1087 			makeVector(OffsetValue(4,  0, makeValueInt32(1)),
   1088 					   OffsetValue(4,  4, makeValueInt32(3)),
   1089 					   OffsetValue(4,  8, makeValueInt32(2)),
   1090 					   OffsetValue(4, 12, makeValueInt32(1))),
   1091 			(FeatureFlags)0,
   1092 		},
   1093 		{
   1094 			"array_size_expression",
   1095 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = 3;"),
   1096 					   SpecConstant(2u, "layout(constant_id = ${ID}) const int sc1 = 5;", 4, makeValueInt32(7))),
   1097 			8,
   1098 			"    int r0;\n"
   1099 			"    int r1;\n",
   1100 
   1101 			"",
   1102 
   1103 			"    int a0[sc0 + 3];\n"
   1104 			"    int a1[sc0 + sc1];\n"
   1105 			"\n"
   1106 			"    const int size0 = sc0 + 3;\n"
   1107 			"    const int size1 = sc0 + sc1;\n"
   1108 			"\n"
   1109 			"    for (int i = 0; i < size0; ++i)\n"
   1110 			"        a0[i] = 3 - i;\n"
   1111 			"    for (int i = 0; i < size1; ++i)\n"
   1112 			"        a1[i] = 5 - i;\n"
   1113 			"\n"
   1114 			"    sb_out.r0 = a0[size0 - 1];\n"
   1115 			"    sb_out.r1 = a1[size1 - 1];\n",
   1116 			makeVector(OffsetValue(4,  0, makeValueInt32(-2)),
   1117 					   OffsetValue(4,  4, makeValueInt32(-4))),
   1118 			(FeatureFlags)0,
   1119 		},
   1120 		{
   1121 			"array_size_spec_const_expression",
   1122 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = 3;"),
   1123 					   SpecConstant(2u, "layout(constant_id = ${ID}) const int sc1 = 5;", 4, makeValueInt32(7))),
   1124 			8,
   1125 			"    int r0;\n"
   1126 			"    int r1;\n",
   1127 
   1128 			"",
   1129 
   1130 			"    const int size0 = sc0 + 3;\n"
   1131 			"    const int size1 = sc0 + sc1;\n"
   1132 			"\n"
   1133 			"    int a0[size0];\n"
   1134 			"    int a1[size1];\n"
   1135 			"\n"
   1136 			"    for (int i = 0; i < size0; ++i)\n"
   1137 			"        a0[i] = 3 - i;\n"
   1138 			"    for (int i = 0; i < size1; ++i)\n"
   1139 			"        a1[i] = 5 - i;\n"
   1140 			"\n"
   1141 			"    sb_out.r0 = a0[size0 - 1];\n"
   1142 			"    sb_out.r1 = a1[size1 - 1];\n",
   1143 			makeVector(OffsetValue(4,  0, makeValueInt32(-2)),
   1144 					   OffsetValue(4,  4, makeValueInt32(-4))),
   1145 			(FeatureFlags)0,
   1146 		},
   1147 		{
   1148 			"array_size_length",
   1149 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = 1;"),
   1150 					   SpecConstant(2u, "layout(constant_id = ${ID}) const int sc1 = 2;", 4, makeValueInt32(4))),
   1151 			8,
   1152 			"    int r0;\n"
   1153 			"    int r1;\n",
   1154 
   1155 			"",
   1156 
   1157 			"    int a0[sc0];\n"
   1158 			"    int a1[sc1];\n"
   1159 			"\n"
   1160 			"    sb_out.r0 = a0.length();\n"
   1161 			"    sb_out.r1 = a1.length();\n",
   1162 			makeVector(OffsetValue(4,  0, makeValueInt32(1)),
   1163 					   OffsetValue(4,  4, makeValueInt32(4))),
   1164 			(FeatureFlags)0,
   1165 		},
   1166 		{
   1167 			"array_size_pass_to_function",
   1168 			makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = 3;"),
   1169 					   SpecConstant(2u, "layout(constant_id = ${ID}) const int sc1 = 1;", 4, makeValueInt32(3))),
   1170 			4,
   1171 			"    int result;\n",
   1172 
   1173 			"int sumArrays (int a0[sc0], int a1[sc1])\n"
   1174 			"{\n"
   1175 			"    int sum = 0;\n"
   1176 			"    for (int i = 0; (i < sc0) && (i < sc1); ++i)\n"
   1177 			"        sum += a0[i] + a1[i];\n"
   1178 			"    return sum;\n"
   1179 			"}\n",
   1180 
   1181 			"    int a0[sc0];\n"
   1182 			"    int a1[sc1];\n"
   1183 			"\n"
   1184 			"    for (int i = 0; i < sc0; ++i)\n"
   1185 			"        a0[i] = i + 1;\n"
   1186 			"    for (int i = 0; i < sc1; ++i)\n"
   1187 			"        a1[i] = i + 2;\n"
   1188 			"\n"
   1189 			"    sb_out.result = sumArrays(a0, a1);\n",
   1190 			makeVector(OffsetValue(4,  0, makeValueInt32(15))),
   1191 			(FeatureFlags)0,
   1192 		},
   1193 	};
   1194 
   1195 	for (int defNdx = 0; defNdx < DE_LENGTH_OF_ARRAY(defs); ++defNdx)
   1196 		testGroup->addChild(new SpecConstantTest(testCtx, shaderStage, defs[defNdx]));
   1197 
   1198 	return testGroup.release();
   1199 }
   1200 
   1201 //! Helper functions internal to make*CompositeCaseDefinition functions.
   1202 namespace composite_case_internal
   1203 {
   1204 
   1205 //! Generate a string like this: "1, 2, sc0, 4" or "true, true, sc0"
   1206 //! castToType = true is useful when type requires more initializer values than we are providing, e.g.:
   1207 //!    vec2(1), vec2(sc0), vec(3)
   1208 std::string generateInitializerListWithSpecConstant (const glu::DataType	type,
   1209 													 const bool				castToType,
   1210 													 const int				idxBegin,
   1211 													 const int				idxEnd,
   1212 													 const std::string&		specConstName,
   1213 													 const int				specConstNdx)
   1214 {
   1215 	std::ostringstream str;
   1216 
   1217 	for (int i = idxBegin; i < idxEnd; ++i)
   1218 	{
   1219 		const std::string iVal = (i == specConstNdx ? specConstName : glu::getDataTypeScalarType(type) == glu::TYPE_BOOL ? "true" : de::toString(i + 1));
   1220 		str << (i != idxBegin ? ", " : "") << (castToType ? de::toString(glu::getDataTypeName(type)) + "(" + iVal + ")" : iVal);
   1221 	}
   1222 
   1223 	return str.str();
   1224 }
   1225 
   1226 std::string generateArrayConstructorString (const glu::DataType	elemType,
   1227 											const int			size1,
   1228 											const int			size2,
   1229 											const std::string&	specConstName,
   1230 											const int			specConstNdx)
   1231 {
   1232 	const bool isArrayOfArray = (size2 > 0);
   1233 	const bool doCast		  = (!isDataTypeScalar(elemType));
   1234 
   1235 	std::ostringstream arrayCtorExpr;
   1236 
   1237 	if (isArrayOfArray)
   1238 	{
   1239 		const std::string padding  (36, ' ');
   1240 		int               idxBegin = 0;
   1241 		int               idxEnd   = size2;
   1242 
   1243 		for (int iterNdx = 0; iterNdx < size1; ++iterNdx)
   1244 		{
   1245 			// Open sub-array ctor
   1246 			arrayCtorExpr << (iterNdx != 0 ? ",\n" + padding : "") << glu::getDataTypeName(elemType) << "[" << size2 << "](";
   1247 
   1248 			// Sub-array constructor elements
   1249 			arrayCtorExpr << generateInitializerListWithSpecConstant(elemType, doCast, idxBegin, idxEnd, specConstName, specConstNdx);
   1250 
   1251 			// Close sub-array ctor, move to next range
   1252 			arrayCtorExpr << ")";
   1253 
   1254 			idxBegin += size2;
   1255 			idxEnd += size2;
   1256 		}
   1257 	}
   1258 	else
   1259 	{
   1260 		// Array constructor elements
   1261 		arrayCtorExpr << generateInitializerListWithSpecConstant(elemType, doCast, 0, size1, specConstName, specConstNdx);
   1262 	}
   1263 
   1264 	return arrayCtorExpr.str();
   1265 }
   1266 
   1267 inline GenericValue makeValue (const glu::DataType type, const int specValue)
   1268 {
   1269 	if (type == glu::TYPE_DOUBLE)
   1270 		return makeValueFloat64(static_cast<double>(specValue));
   1271 	else if (type == glu::TYPE_FLOAT)
   1272 		return makeValueFloat32(static_cast<float>(specValue));
   1273 	else
   1274 		return makeValueInt32(specValue);
   1275 }
   1276 
   1277 deUint32 getDataTypeScalarSizeBytes (const glu::DataType dataType)
   1278 {
   1279 	switch (getDataTypeScalarType(dataType))
   1280 	{
   1281 		case glu::TYPE_FLOAT:
   1282 		case glu::TYPE_INT:
   1283 		case glu::TYPE_UINT:
   1284 		case glu::TYPE_BOOL:
   1285 			return 4;
   1286 
   1287 		case glu::TYPE_DOUBLE:
   1288 			return 8;
   1289 
   1290 		default:
   1291 			DE_ASSERT(false);
   1292 			return 0;
   1293 	}
   1294 }
   1295 
   1296 //! This applies to matrices/vectors/array cases. dataType must be a basic type.
   1297 std::vector<OffsetValue> computeExpectedValues (const int specValue, const glu::DataType dataType, const int numCombinations)
   1298 {
   1299 	DE_ASSERT(glu::isDataTypeScalar(dataType));
   1300 
   1301 	std::vector<OffsetValue> expectedValues;
   1302 
   1303 	for (int combNdx = 0; combNdx < numCombinations; ++combNdx)
   1304 	{
   1305 		int sum = 0;
   1306 		for (int i = 0; i < numCombinations; ++i)
   1307 			sum += (i == combNdx ? specValue : dataType == glu::TYPE_BOOL ? 1 : (i + 1));
   1308 
   1309 		const int dataSize = getDataTypeScalarSizeBytes(dataType);
   1310 		expectedValues.push_back(OffsetValue(dataSize, dataSize * combNdx, makeValue(dataType, sum)));
   1311 	}
   1312 
   1313 	return expectedValues;
   1314 }
   1315 
   1316 inline std::string getFirstDataElementSubscriptString (const glu::DataType type)
   1317 {
   1318 	// Grab the first element of a matrix/vector, if dealing with non-basic types.
   1319 	return (isDataTypeMatrix(type) ? "[0][0]" : isDataTypeVector(type) ? "[0]" : "");
   1320 }
   1321 
   1322 //! This code will go into the main function.
   1323 std::string generateShaderChecksumComputationCode (const glu::DataType	elemType,
   1324 												   const std::string&	varName,
   1325 												   const std::string&	accumType,
   1326 												   const int			size1,
   1327 												   const int			size2,
   1328 												   const int			numCombinations)
   1329 {
   1330 	std::ostringstream mainCode;
   1331 
   1332 	// Generate main code to calculate checksums for each array
   1333 	for (int combNdx = 0; combNdx < numCombinations; ++combNdx)
   1334 		mainCode << "    "<< accumType << " sum_" << varName << combNdx << " = " << accumType << "(0);\n";
   1335 
   1336 	if (size2 > 0)
   1337 	{
   1338 		mainCode << "\n"
   1339 					<< "    for (int i = 0; i < " << size1 << "; ++i)\n"
   1340 					<< "    for (int j = 0; j < " << size2 << "; ++j)\n"
   1341 					<< "    {\n";
   1342 
   1343 		for (int combNdx = 0; combNdx < numCombinations; ++combNdx)
   1344 			mainCode << "        sum_" << varName << combNdx << " += " << accumType << "("
   1345 					 << varName << combNdx << "[i][j]" << getFirstDataElementSubscriptString(elemType) << ");\n";
   1346 	}
   1347 	else
   1348 	{
   1349 		mainCode << "\n"
   1350 					<< "    for (int i = 0; i < " << size1 << "; ++i)\n"
   1351 					<< "    {\n";
   1352 
   1353 		for (int combNdx = 0; combNdx < numCombinations; ++combNdx)
   1354 			mainCode << "        sum_" << varName << combNdx << " += " << accumType << "("
   1355 					 << varName << combNdx << "[i]" << getFirstDataElementSubscriptString(elemType) << ");\n";
   1356 	}
   1357 
   1358 	mainCode << "    }\n"
   1359 				<< "\n";
   1360 
   1361 	for (int combNdx = 0; combNdx < numCombinations; ++combNdx)
   1362 		mainCode << "    sb_out.result[" << combNdx << "] = sum_" << varName << combNdx << ";\n";
   1363 
   1364 	return mainCode.str();
   1365 }
   1366 
   1367 SpecConstant makeSpecConstant (const std::string specConstName, const deUint32 specConstId, const glu::DataType type, const int specValue)
   1368 {
   1369 	DE_ASSERT(glu::isDataTypeScalar(type));
   1370 
   1371 	const std::string typeName(glu::getDataTypeName(type));
   1372 
   1373 	return SpecConstant(
   1374 		specConstId,
   1375 		"layout(constant_id = ${ID}) const " + typeName + " " + specConstName + " = " + typeName + "(1);",
   1376 		getDataTypeScalarSizeBytes(type), makeValue(type, specValue));
   1377 }
   1378 
   1379 } // composite_case_internal ns
   1380 
   1381 //! Generate a CaseDefinition for a composite test using a matrix or vector (a 1-column matrix)
   1382 CaseDefinition makeMatrixVectorCompositeCaseDefinition (const glu::DataType type)
   1383 {
   1384 	using namespace composite_case_internal;
   1385 
   1386 	DE_ASSERT(!glu::isDataTypeScalar(type));
   1387 
   1388 	const std::string   varName         = (glu::isDataTypeMatrix(type) ? "m" : "v");
   1389 	const int           numCombinations = getDataTypeScalarSize(type);
   1390 	const glu::DataType scalarType      = glu::getDataTypeScalarType(type);
   1391 	const std::string   typeName        = glu::getDataTypeName(type);
   1392 	const bool			isConst		= (scalarType != glu::TYPE_FLOAT) && (scalarType != glu::TYPE_DOUBLE);
   1393 
   1394 	std::ostringstream globalCode;
   1395 	{
   1396 		// Build N matrices/vectors with specialization constant inserted at various locations in the constructor.
   1397 		for (int combNdx = 0; combNdx < numCombinations; ++combNdx)
   1398 			globalCode << ( isConst ? "const " : "" ) << typeName << " " << varName << combNdx << " = " << typeName << "("
   1399 					   << generateInitializerListWithSpecConstant(type, false, 0, numCombinations, "sc0", combNdx) << ");\n";
   1400 	}
   1401 
   1402 	const bool        isBoolElement = (scalarType == glu::TYPE_BOOL);
   1403 	const int         specValue     = (isBoolElement ? 0 : 42);
   1404 	const std::string accumType     = glu::getDataTypeName(isBoolElement ? glu::TYPE_INT : scalarType);
   1405 
   1406 	const int size1 = glu::isDataTypeMatrix(type) ? glu::getDataTypeMatrixNumColumns(type) : glu::getDataTypeNumComponents(type);
   1407 	const int size2 = glu::isDataTypeMatrix(type) ? glu::getDataTypeMatrixNumRows(type)    : 0;
   1408 
   1409 	const CaseDefinition def =
   1410 	{
   1411 		typeName,
   1412 		makeVector(makeSpecConstant("sc0", 1u, scalarType, specValue)),
   1413 		static_cast<VkDeviceSize>(getDataTypeScalarSizeBytes(type) * numCombinations),
   1414 		"    " + accumType + " result[" + de::toString(numCombinations) + "];\n",
   1415 		globalCode.str(),
   1416 		generateShaderChecksumComputationCode(scalarType, varName, accumType, size1, size2, numCombinations),
   1417 		computeExpectedValues(specValue, scalarType, numCombinations),
   1418 		(scalarType == glu::TYPE_DOUBLE ? (FeatureFlags)FEATURE_SHADER_FLOAT_64 : (FeatureFlags)0),
   1419 	};
   1420 	return def;
   1421 }
   1422 
   1423 //! Generate a CaseDefinition for a composite test using an array, or an array of array.
   1424 //! If (size1, size2) = (N, 0) -> type array[N]
   1425 //!                   = (N, M) -> type array[N][M]
   1426 CaseDefinition makeArrayCompositeCaseDefinition (const glu::DataType elemType, const int size1, const int size2 = 0)
   1427 {
   1428 	using namespace composite_case_internal;
   1429 
   1430 	DE_ASSERT(size1 > 0);
   1431 
   1432 	const bool        isArrayOfArray  = (size2 > 0);
   1433 	const std::string varName		  = "a";
   1434 	const std::string arraySizeDecl   = "[" + de::toString(size1) + "]" + (isArrayOfArray ? "[" + de::toString(size2) + "]" : "");
   1435 	const int         numCombinations = (isArrayOfArray ? size1 * size2 : size1);
   1436 	const std::string elemTypeName    (glu::getDataTypeName(elemType));
   1437 
   1438 	std::ostringstream globalCode;
   1439 	{
   1440 		// Create several arrays with specialization constant inserted in different positions.
   1441 		for (int combNdx = 0; combNdx < numCombinations; ++combNdx)
   1442 			globalCode << elemTypeName << " " << varName << combNdx << arraySizeDecl << " = "
   1443 					   << elemTypeName << arraySizeDecl << "(" << generateArrayConstructorString(elemType, size1, size2, "sc0", combNdx) << ");\n";
   1444 	}
   1445 
   1446 	const glu::DataType scalarType = glu::getDataTypeScalarType(elemType);
   1447 	const bool          isBoolData = (scalarType == glu::TYPE_BOOL);
   1448 	const int           specValue  = (isBoolData ? 0 : 19);
   1449 	const std::string   caseName   = (isArrayOfArray ? "array_" : "") + elemTypeName;
   1450 	const std::string   accumType  = (glu::getDataTypeName(isBoolData ? glu::TYPE_INT : scalarType));
   1451 
   1452 	const CaseDefinition def =
   1453 	{
   1454 		caseName,
   1455 		makeVector(makeSpecConstant("sc0", 1u, scalarType, specValue)),
   1456 		static_cast<VkDeviceSize>(getDataTypeScalarSizeBytes(elemType) * numCombinations),
   1457 		"    " + accumType + " result[" + de::toString(numCombinations) + "];\n",
   1458 		globalCode.str(),
   1459 		generateShaderChecksumComputationCode(elemType, varName, accumType, size1, size2, numCombinations),
   1460 		computeExpectedValues(specValue, scalarType, numCombinations),
   1461 		(scalarType == glu::TYPE_DOUBLE ? (FeatureFlags)FEATURE_SHADER_FLOAT_64 : (FeatureFlags)0),
   1462 	};
   1463 	return def;
   1464 }
   1465 
   1466 //! A basic struct case, where one member is a specialization constant, or a specialization constant composite
   1467 //! (a matrix/vector with a spec. const. element).
   1468 CaseDefinition makeStructCompositeCaseDefinition (const glu::DataType memberType)
   1469 {
   1470 	using namespace composite_case_internal;
   1471 
   1472 	std::ostringstream globalCode;
   1473 	{
   1474 		globalCode << "struct Data {\n"
   1475 				   << "    int   i;\n"
   1476 				   << "    float f;\n"
   1477 				   << "    bool  b;\n"
   1478 				   << "    " << glu::getDataTypeName(memberType) << " sc;\n"
   1479 				   << "    uint  ui;\n"
   1480 				   << "};\n"
   1481 				   << "\n"
   1482 				   << "Data s0 = Data(3, 2.0, true, " << glu::getDataTypeName(memberType) << "(sc0), 8u);\n";
   1483 	}
   1484 
   1485 	const glu::DataType scalarType   = glu::getDataTypeScalarType(memberType);
   1486 	const bool          isBoolData   = (scalarType == glu::TYPE_BOOL);
   1487 	const int           specValue    = (isBoolData ? 0 : 23);
   1488 	const int           checksum     = (3 + 2 + 1 + specValue + 8);  // matches the shader code
   1489 	const glu::DataType accumType    = (isBoolData ? glu::TYPE_INT : scalarType);
   1490 	const std::string   accumTypeStr = glu::getDataTypeName(accumType);
   1491 
   1492 	std::ostringstream mainCode;
   1493 	{
   1494 		mainCode << "    " << accumTypeStr << " sum_s0 = " << accumTypeStr << "(0);\n"
   1495 				 << "\n"
   1496 				 << "    sum_s0 += " << accumTypeStr << "(s0.i);\n"
   1497 				 << "    sum_s0 += " << accumTypeStr << "(s0.f);\n"
   1498 				 << "    sum_s0 += " << accumTypeStr << "(s0.b);\n"
   1499 				 << "    sum_s0 += " << accumTypeStr << "(s0.sc" << getFirstDataElementSubscriptString(memberType) << ");\n"
   1500 				 << "    sum_s0 += " << accumTypeStr << "(s0.ui);\n"
   1501 				 << "\n"
   1502 				 << "    sb_out.result = sum_s0;\n";
   1503 	}
   1504 
   1505 	const std::string caseName = glu::getDataTypeName(memberType);
   1506 
   1507 	const CaseDefinition def =
   1508 	{
   1509 		caseName,
   1510 		makeVector(makeSpecConstant("sc0", 1u, scalarType, specValue)),
   1511 		getDataTypeScalarSizeBytes(accumType),
   1512 		"    " + accumTypeStr + " result;\n",
   1513 		globalCode.str(),
   1514 		mainCode.str(),
   1515 		makeVector(OffsetValue(getDataTypeScalarSizeBytes(memberType), 0, makeValue(scalarType, checksum))),
   1516 		(scalarType == glu::TYPE_DOUBLE ? (FeatureFlags)FEATURE_SHADER_FLOAT_64 : (FeatureFlags)0),
   1517 	};
   1518 	return def;
   1519 }
   1520 
   1521 //! Specialization constants used in composites.
   1522 tcu::TestCaseGroup* createCompositeTests (tcu::TestContext& testCtx, const VkShaderStageFlagBits shaderStage)
   1523 {
   1524 	de::MovePtr<tcu::TestCaseGroup> compositeTests (new tcu::TestCaseGroup(testCtx, "composite", "specialization constants usage in composite types"));
   1525 
   1526 	// Vectors
   1527 	{
   1528 		de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "vector", ""));
   1529 
   1530 		const glu::DataType types[] =
   1531 		{
   1532 			glu::TYPE_FLOAT_VEC2,
   1533 			glu::TYPE_FLOAT_VEC3,
   1534 			glu::TYPE_FLOAT_VEC4,
   1535 
   1536 			glu::TYPE_DOUBLE_VEC2,
   1537 			glu::TYPE_DOUBLE_VEC3,
   1538 			glu::TYPE_DOUBLE_VEC4,
   1539 
   1540 			glu::TYPE_BOOL_VEC2,
   1541 			glu::TYPE_BOOL_VEC3,
   1542 			glu::TYPE_BOOL_VEC4,
   1543 
   1544 			glu::TYPE_INT_VEC2,
   1545 			glu::TYPE_INT_VEC3,
   1546 			glu::TYPE_INT_VEC4,
   1547 
   1548 			glu::TYPE_UINT_VEC2,
   1549 			glu::TYPE_UINT_VEC3,
   1550 			glu::TYPE_UINT_VEC4,
   1551 		};
   1552 		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); ++typeNdx)
   1553 			group->addChild(new SpecConstantTest(testCtx, shaderStage, makeMatrixVectorCompositeCaseDefinition(types[typeNdx])));
   1554 
   1555 		compositeTests->addChild(group.release());
   1556 	}
   1557 
   1558 	// Matrices
   1559 	{
   1560 		de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "matrix", ""));
   1561 
   1562 		const glu::DataType types[] =
   1563 		{
   1564 			glu::TYPE_FLOAT_MAT2,
   1565 			glu::TYPE_FLOAT_MAT2X3,
   1566 			glu::TYPE_FLOAT_MAT2X4,
   1567 			glu::TYPE_FLOAT_MAT3X2,
   1568 			glu::TYPE_FLOAT_MAT3,
   1569 			glu::TYPE_FLOAT_MAT3X4,
   1570 			glu::TYPE_FLOAT_MAT4X2,
   1571 			glu::TYPE_FLOAT_MAT4X3,
   1572 			glu::TYPE_FLOAT_MAT4,
   1573 
   1574 			glu::TYPE_DOUBLE_MAT2,
   1575 			glu::TYPE_DOUBLE_MAT2X3,
   1576 			glu::TYPE_DOUBLE_MAT2X4,
   1577 			glu::TYPE_DOUBLE_MAT3X2,
   1578 			glu::TYPE_DOUBLE_MAT3,
   1579 			glu::TYPE_DOUBLE_MAT3X4,
   1580 			glu::TYPE_DOUBLE_MAT4X2,
   1581 			glu::TYPE_DOUBLE_MAT4X3,
   1582 			glu::TYPE_DOUBLE_MAT4,
   1583 		};
   1584 		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); ++typeNdx)
   1585 			group->addChild(new SpecConstantTest(testCtx, shaderStage, makeMatrixVectorCompositeCaseDefinition(types[typeNdx])));
   1586 
   1587 		compositeTests->addChild(group.release());
   1588 	}
   1589 
   1590 	const glu::DataType allTypes[] =
   1591 	{
   1592 		glu::TYPE_FLOAT,
   1593 		glu::TYPE_FLOAT_VEC2,
   1594 		glu::TYPE_FLOAT_VEC3,
   1595 		glu::TYPE_FLOAT_VEC4,
   1596 		glu::TYPE_FLOAT_MAT2,
   1597 		glu::TYPE_FLOAT_MAT2X3,
   1598 		glu::TYPE_FLOAT_MAT2X4,
   1599 		glu::TYPE_FLOAT_MAT3X2,
   1600 		glu::TYPE_FLOAT_MAT3,
   1601 		glu::TYPE_FLOAT_MAT3X4,
   1602 		glu::TYPE_FLOAT_MAT4X2,
   1603 		glu::TYPE_FLOAT_MAT4X3,
   1604 		glu::TYPE_FLOAT_MAT4,
   1605 
   1606 		glu::TYPE_DOUBLE,
   1607 		glu::TYPE_DOUBLE_VEC2,
   1608 		glu::TYPE_DOUBLE_VEC3,
   1609 		glu::TYPE_DOUBLE_VEC4,
   1610 		glu::TYPE_DOUBLE_MAT2,
   1611 		glu::TYPE_DOUBLE_MAT2X3,
   1612 		glu::TYPE_DOUBLE_MAT2X4,
   1613 		glu::TYPE_DOUBLE_MAT3X2,
   1614 		glu::TYPE_DOUBLE_MAT3,
   1615 		glu::TYPE_DOUBLE_MAT3X4,
   1616 		glu::TYPE_DOUBLE_MAT4X2,
   1617 		glu::TYPE_DOUBLE_MAT4X3,
   1618 		glu::TYPE_DOUBLE_MAT4,
   1619 
   1620 		glu::TYPE_INT,
   1621 		glu::TYPE_INT_VEC2,
   1622 		glu::TYPE_INT_VEC3,
   1623 		glu::TYPE_INT_VEC4,
   1624 
   1625 		glu::TYPE_UINT,
   1626 		glu::TYPE_UINT_VEC2,
   1627 		glu::TYPE_UINT_VEC3,
   1628 		glu::TYPE_UINT_VEC4,
   1629 
   1630 		glu::TYPE_BOOL,
   1631 		glu::TYPE_BOOL_VEC2,
   1632 		glu::TYPE_BOOL_VEC3,
   1633 		glu::TYPE_BOOL_VEC4,
   1634 	};
   1635 
   1636 	// Array cases
   1637 	{
   1638 		de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "array", ""));
   1639 
   1640 		// Array of T
   1641 		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(allTypes); ++typeNdx)
   1642 			group->addChild(new SpecConstantTest(testCtx, shaderStage, makeArrayCompositeCaseDefinition(allTypes[typeNdx], 3)));
   1643 
   1644 		// Array of array of T
   1645 		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(allTypes); ++typeNdx)
   1646 			group->addChild(new SpecConstantTest(testCtx, shaderStage, makeArrayCompositeCaseDefinition(allTypes[typeNdx], 3, 2)));
   1647 
   1648 		// Special case - array of struct
   1649 		{
   1650 			const int checksum = (3 + 2 + 1) + (1 + 5 + 1) + (1 + 2 + 0);
   1651 			const CaseDefinition def =
   1652 			{
   1653 				"struct",
   1654 				makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int   sc0 = 1;",    4, makeValueInt32  (3)),
   1655 						   SpecConstant(2u, "layout(constant_id = ${ID}) const float sc1 = 1.0;",  4, makeValueFloat32(5.0f)),
   1656 						   SpecConstant(3u, "layout(constant_id = ${ID}) const bool  sc2 = true;", 4, makeValueBool32 (false))),
   1657 				4,
   1658 				"    int result;\n",
   1659 
   1660 				"struct Data {\n"
   1661 				"    int   x;\n"
   1662 				"    float y;\n"
   1663 				"    bool  z;\n"
   1664 				"};\n"
   1665 				"\n"
   1666 				"Data a0[3] = Data[3](Data(sc0, 2.0, true), Data(1, sc1, true), Data(1, 2.0, sc2));\n",
   1667 
   1668 				"    int sum_a0 = 0;\n"
   1669 				"\n"
   1670 				"    for (int i = 0; i < 3; ++i)\n"
   1671 				"        sum_a0 += int(a0[i].x) + int(a0[i].y) + int(a0[i].z);\n"
   1672 				"\n"
   1673 				"    sb_out.result = sum_a0;\n",
   1674 
   1675 				makeVector(OffsetValue(4,  0, makeValueInt32(checksum))),
   1676 				(FeatureFlags)0,
   1677 			};
   1678 
   1679 			group->addChild(new SpecConstantTest(testCtx, shaderStage, def));
   1680 		}
   1681 
   1682 		compositeTests->addChild(group.release());
   1683 	}
   1684 
   1685 	// Struct cases
   1686 	{
   1687 		de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "struct", ""));
   1688 
   1689 		// Struct with one member being a specialization constant (or spec. const. composite) of a given type
   1690 		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(allTypes); ++typeNdx)
   1691 			group->addChild(new SpecConstantTest(testCtx, shaderStage, makeStructCompositeCaseDefinition(allTypes[typeNdx])));
   1692 
   1693 		// Special case - struct with array
   1694 		{
   1695 			const int checksum = (1 + 2 + 31 + 4 + 0);
   1696 			const CaseDefinition def =
   1697 			{
   1698 				"array",
   1699 				makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const float sc0 = 1.0;",  4, makeValueFloat32(31.0f))),
   1700 				4,
   1701 				"    float result;\n",
   1702 
   1703 				"struct Data {\n"
   1704 				"    int  i;\n"
   1705 				"    vec3 sc[3];\n"
   1706 				"    bool b;\n"
   1707 				"};\n"
   1708 				"\n"
   1709 				"Data s0 = Data(1, vec3[3](vec3(2.0), vec3(sc0), vec3(4.0)), false);\n",
   1710 
   1711 				"    float sum_s0 = 0;\n"
   1712 				"\n"
   1713 				"    sum_s0 += float(s0.i);\n"
   1714 				"    sum_s0 += float(s0.sc[0][0]);\n"
   1715 				"    sum_s0 += float(s0.sc[1][0]);\n"
   1716 				"    sum_s0 += float(s0.sc[2][0]);\n"
   1717 				"    sum_s0 += float(s0.b);\n"
   1718 				"\n"
   1719 				"    sb_out.result = sum_s0;\n",
   1720 
   1721 				makeVector(OffsetValue(4,  0, makeValueFloat32(static_cast<float>(checksum)))),
   1722 				(FeatureFlags)0,
   1723 			};
   1724 
   1725 			group->addChild(new SpecConstantTest(testCtx, shaderStage, def));
   1726 		}
   1727 
   1728 		// Special case - struct of struct
   1729 		{
   1730 			const int checksum = (1 + 2 + 11 + 4 + 1);
   1731 			const CaseDefinition def =
   1732 			{
   1733 				"struct",
   1734 				makeVector(SpecConstant(1u, "layout(constant_id = ${ID}) const int sc0 = 1;",  4, makeValueInt32(11))),
   1735 				4,
   1736 				"    int result;\n",
   1737 
   1738 				"struct Nested {\n"
   1739 				"    vec2  v;\n"
   1740 				"    int   sc;\n"
   1741 				"    float f;\n"
   1742 				"};\n"
   1743 				"\n"
   1744 				"struct Data {\n"
   1745 				"    uint   ui;\n"
   1746 				"    Nested s;\n"
   1747 				"    bool   b;\n"
   1748 				"};\n"
   1749 				"\n"
   1750 				"Data s0 = Data(1u, Nested(vec2(2.0), sc0, 4.0), true);\n",
   1751 
   1752 				"    int sum_s0 = 0;\n"
   1753 				"\n"
   1754 				"    sum_s0 += int(s0.ui);\n"
   1755 				"    sum_s0 += int(s0.s.v[0]);\n"
   1756 				"    sum_s0 += int(s0.s.sc);\n"
   1757 				"    sum_s0 += int(s0.s.f);\n"
   1758 				"    sum_s0 += int(s0.b);\n"
   1759 				"\n"
   1760 				"    sb_out.result = sum_s0;\n",
   1761 
   1762 				makeVector(OffsetValue(4,  0, makeValueInt32(checksum))),
   1763 				(FeatureFlags)0,
   1764 			};
   1765 
   1766 			group->addChild(new SpecConstantTest(testCtx, shaderStage, def));
   1767 		}
   1768 
   1769 		compositeTests->addChild(group.release());
   1770 	}
   1771 
   1772 	return compositeTests.release();
   1773 }
   1774 
   1775 } // anonymous ns
   1776 
   1777 tcu::TestCaseGroup* createSpecConstantTests (tcu::TestContext& testCtx)
   1778 {
   1779 	de::MovePtr<tcu::TestCaseGroup> allTests (new tcu::TestCaseGroup(testCtx, "spec_constant", "Specialization constants tests"));
   1780 	de::MovePtr<tcu::TestCaseGroup> graphicsGroup (new tcu::TestCaseGroup(testCtx, "graphics", ""));
   1781 
   1782 	struct StageDef
   1783 	{
   1784 		tcu::TestCaseGroup*		parentGroup;
   1785 		const char*				name;
   1786 		VkShaderStageFlagBits	stage;
   1787 	};
   1788 
   1789 	const StageDef stages[] =
   1790 	{
   1791 		{ graphicsGroup.get(),	"vertex",		VK_SHADER_STAGE_VERTEX_BIT					},
   1792 		{ graphicsGroup.get(),	"fragment",		VK_SHADER_STAGE_FRAGMENT_BIT				},
   1793 		{ graphicsGroup.get(),	"tess_control",	VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT	},
   1794 		{ graphicsGroup.get(),	"tess_eval",	VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT	},
   1795 		{ graphicsGroup.get(),	"geometry",		VK_SHADER_STAGE_GEOMETRY_BIT				},
   1796 		{ allTests.get(),		"compute",		VK_SHADER_STAGE_COMPUTE_BIT					},
   1797 	};
   1798 
   1799 	allTests->addChild(graphicsGroup.release());
   1800 
   1801 	for (int stageNdx = 0; stageNdx < DE_LENGTH_OF_ARRAY(stages); ++stageNdx)
   1802 	{
   1803 		const StageDef& stage = stages[stageNdx];
   1804 		de::MovePtr<tcu::TestCaseGroup> stageGroup (new tcu::TestCaseGroup(testCtx, stage.name, ""));
   1805 
   1806 		stageGroup->addChild(createDefaultValueTests       (testCtx, stage.stage));
   1807 		stageGroup->addChild(createBasicSpecializationTests(testCtx, stage.stage));
   1808 		stageGroup->addChild(createBuiltInOverrideTests    (testCtx, stage.stage));
   1809 		stageGroup->addChild(createExpressionTests         (testCtx, stage.stage));
   1810 		stageGroup->addChild(createCompositeTests          (testCtx, stage.stage));
   1811 
   1812 		if (stage.stage == VK_SHADER_STAGE_COMPUTE_BIT)
   1813 			stageGroup->addChild(createWorkGroupSizeTests(testCtx));
   1814 
   1815 		stage.parentGroup->addChild(stageGroup.release());
   1816 	}
   1817 
   1818 	return allTests.release();
   1819 }
   1820 
   1821 } // pipeline
   1822 } // vkt
   1823