Home | History | Annotate | Download | only in vulkan
      1 /*-------------------------------------------------------------------------
      2  * Vulkan CTS Framework
      3  * --------------------
      4  *
      5  * Copyright (c) 2015 Google 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 Program utilities.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #if defined(DEQP_HAVE_SPIRV_TOOLS)
     25 #include "spirv-tools/optimizer.hpp"
     26 #endif
     27 
     28 #include "qpInfo.h"
     29 
     30 #include "vkPrograms.hpp"
     31 #include "vkShaderToSpirV.hpp"
     32 #include "vkSpirVAsm.hpp"
     33 #include "vkRefUtil.hpp"
     34 
     35 #include "deMutex.hpp"
     36 #include "deFilePath.hpp"
     37 #include "deArrayUtil.hpp"
     38 #include "deMemory.h"
     39 #include "deInt32.h"
     40 
     41 #include "tcuCommandLine.hpp"
     42 
     43 #include <map>
     44 
     45 namespace vk
     46 {
     47 
     48 using std::string;
     49 using std::vector;
     50 using std::map;
     51 
     52 #if defined(DE_DEBUG) && defined(DEQP_HAVE_SPIRV_TOOLS)
     53 #	define VALIDATE_BINARIES	true
     54 #else
     55 #	define VALIDATE_BINARIES	false
     56 #endif
     57 
     58 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
     59 
     60 // ProgramBinary
     61 
     62 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
     63 	: m_format	(format)
     64 	, m_binary	(binary, binary+binarySize)
     65 {
     66 }
     67 
     68 // Utils
     69 
     70 namespace
     71 {
     72 
     73 bool isNativeSpirVBinaryEndianness (void)
     74 {
     75 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
     76 	return true;
     77 #else
     78 	return false;
     79 #endif
     80 }
     81 
     82 bool isSaneSpirVBinary (const ProgramBinary& binary)
     83 {
     84 	const deUint32	spirvMagicWord	= 0x07230203;
     85 	const deUint32	spirvMagicBytes	= isNativeSpirVBinaryEndianness()
     86 									? spirvMagicWord
     87 									: deReverseBytes32(spirvMagicWord);
     88 
     89 	DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
     90 
     91 	if (binary.getSize() % sizeof(deUint32) != 0)
     92 		return false;
     93 
     94 	if (binary.getSize() < sizeof(deUint32))
     95 		return false;
     96 
     97 	if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
     98 		return false;
     99 
    100 	return true;
    101 }
    102 
    103 #if defined(DEQP_HAVE_SPIRV_TOOLS)
    104 
    105 void optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
    106 {
    107 	spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
    108 
    109 	// Map SpirvVersion with spv_target_env:
    110 	switch (spirvVersion)
    111 	{
    112 		case SPIRV_VERSION_1_0: targetEnv = SPV_ENV_VULKAN_1_0;	break;
    113 		case SPIRV_VERSION_1_1:
    114 		case SPIRV_VERSION_1_2:
    115 		case SPIRV_VERSION_1_3: targetEnv = SPV_ENV_VULKAN_1_1;	break;
    116 		default:
    117 			TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
    118 	}
    119 
    120 	spvtools::Optimizer optimizer(targetEnv);
    121 
    122 	switch (optimizationRecipe)
    123 	{
    124 		case 1:
    125 			// The example recipe from:
    126 			// https://www.lunarg.com/wp-content/uploads/2017/08/SPIR-V-Shader-Size-Reduction-Using-spirv-opt_v1.0.pdf
    127 			optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());				// --inline-entry-points-exhaustive
    128 			optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());		// --convert-local-access-chains
    129 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());// --eliminate-local-single-block
    130 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());			// --eliminate-local-single-store
    131 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());			// --eliminate-insert-extract
    132 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());				// --eliminate-dead-code-aggressive
    133 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());				// --eliminate-dead-branches
    134 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());					// --merge-blocks
    135 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());// --eliminate-local-single-block
    136 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());			// --eliminate-local-single-store
    137 			optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());			// --eliminate-local-multi-store
    138 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());			// --eliminate-insert-extract
    139 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());				// --eliminate-dead-code-aggressive
    140 			optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());			// --eliminate-common-uniform
    141 			break;
    142 		case 2: // RegisterPerformancePasses from commandline optimizer tool october 2017
    143 			optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
    144 			optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
    145 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    146 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    147 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    148 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    149 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    150 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());
    151 			optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
    152 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    153 			optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());
    154 			break;
    155 		case 3: // RegisterSizePasses from commandline optimizer tool october 2017
    156 			optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
    157 			optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
    158 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    159 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    160 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    161 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    162 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    163 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());
    164 			optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
    165 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    166 			optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());
    167 			break;
    168 		case 4: // RegisterLegalizationPasses from commandline optimizer tool April 2018
    169 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    170 			optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
    171 			optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
    172 			optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass());
    173 			optimizer.RegisterPass(spvtools::CreatePrivateToLocalPass());
    174 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    175 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    176 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    177 			optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
    178 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    179 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    180 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    181 			optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
    182 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    183 			optimizer.RegisterPass(spvtools::CreateCCPPass());
    184 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    185 			optimizer.RegisterPass(spvtools::CreateSimplificationPass());
    186 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    187 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    188 			optimizer.RegisterPass(spvtools::CreateCopyPropagateArraysPass());
    189 			optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
    190 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    191 			break;
    192 		case 5: // RegisterPerformancePasses from commandline optimizer tool April 2018
    193 			optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
    194 			optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
    195 			optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
    196 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    197 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    198 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    199 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    200 			optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
    201 			optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
    202 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    203 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    204 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    205 			optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
    206 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    207 			optimizer.RegisterPass(spvtools::CreateCCPPass());
    208 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    209 			optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
    210 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    211 			optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
    212 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    213 			optimizer.RegisterPass(spvtools::CreateSimplificationPass());
    214 			optimizer.RegisterPass(spvtools::CreateIfConversionPass());
    215 			optimizer.RegisterPass(spvtools::CreateCopyPropagateArraysPass());
    216 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    217 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());
    218 			optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
    219 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    220 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());
    221 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    222 				// comment from tool:
    223 			    // Currently exposing driver bugs resulting in crashes (#946)
    224 				// .RegisterPass(CreateCommonUniformElimPass())
    225 			break;
    226 		case 6: // RegisterPerformancePasses from commandline optimizer tool April 2018 with CreateCommonUniformElimPass
    227 			optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
    228 			optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
    229 			optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
    230 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    231 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    232 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    233 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    234 			optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
    235 			optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
    236 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    237 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    238 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    239 			optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
    240 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    241 			optimizer.RegisterPass(spvtools::CreateCCPPass());
    242 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    243 			optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
    244 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    245 			optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
    246 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    247 			optimizer.RegisterPass(spvtools::CreateSimplificationPass());
    248 			optimizer.RegisterPass(spvtools::CreateIfConversionPass());
    249 			optimizer.RegisterPass(spvtools::CreateCopyPropagateArraysPass());
    250 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    251 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());
    252 			optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
    253 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    254 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());
    255 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    256 			optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());
    257 			break;
    258 		case 7: // RegisterSizePasses from commandline optimizer tool April 2018
    259 			optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
    260 			optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
    261 			optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
    262 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    263 			optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
    264 			optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
    265 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    266 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    267 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    268 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    269 			optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
    270 			optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
    271 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    272 			optimizer.RegisterPass(spvtools::CreateCCPPass());
    273 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    274 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    275 			optimizer.RegisterPass(spvtools::CreateIfConversionPass());
    276 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    277 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());
    278 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    279 			optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
    280 			optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
    281 			optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
    282 				// comment from tool:
    283 				// Currently exposing driver bugs resulting in crashes (#946)
    284 				// .RegisterPass(CreateCommonUniformElimPass())
    285 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    286 			break;
    287 		case 8: // RegisterSizePasses from commandline optimizer tool April 2018 with CreateCommonUniformElimPass
    288 			optimizer.RegisterPass(spvtools::CreateRemoveDuplicatesPass());
    289 			optimizer.RegisterPass(spvtools::CreateMergeReturnPass());
    290 			optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass());
    291 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    292 			optimizer.RegisterPass(spvtools::CreateScalarReplacementPass());
    293 			optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass());
    294 			optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass());
    295 			optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass());
    296 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    297 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    298 			optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
    299 			optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass());
    300 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    301 			optimizer.RegisterPass(spvtools::CreateCCPPass());
    302 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    303 			optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass());
    304 			optimizer.RegisterPass(spvtools::CreateIfConversionPass());
    305 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    306 			optimizer.RegisterPass(spvtools::CreateBlockMergePass());
    307 			optimizer.RegisterPass(spvtools::CreateInsertExtractElimPass());
    308 			optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass());
    309 			optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass());
    310 			optimizer.RegisterPass(spvtools::CreateCFGCleanupPass());
    311 			optimizer.RegisterPass(spvtools::CreateCommonUniformElimPass());
    312 			optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass());
    313 			break;
    314 		default:
    315 			TCU_THROW(InternalError, "Unknown optimization recipe requested");
    316 	}
    317 
    318 	spvtools::OptimizerOptions optimizer_options;
    319 	optimizer_options.set_run_validator(false);
    320 	const bool ok = optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
    321 
    322 	if (!ok)
    323 		TCU_THROW(InternalError, "Optimizer call failed");
    324 }
    325 
    326 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
    327 {
    328 	DE_ASSERT(!binary.empty());
    329 
    330 	if (isNativeSpirVBinaryEndianness())
    331 		return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
    332 	else
    333 		TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
    334 }
    335 
    336 #endif // defined(DEQP_HAVE_SPIRV_TOOLS)
    337 
    338 } // anonymous
    339 
    340 #if defined(DEQP_HAVE_SPIRV_TOOLS)
    341 
    342 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
    343 {
    344 	std::ostringstream validationLog;
    345 
    346 	if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
    347 	{
    348 		buildInfo->program.linkOk	 = false;
    349 		buildInfo->program.infoLog	+= "\n" + validationLog.str();
    350 
    351 		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
    352 	}
    353 }
    354 
    355 void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
    356 {
    357 	std::ostringstream validationLog;
    358 
    359 	if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
    360 	{
    361 		buildInfo->compileOk = false;
    362 		buildInfo->infoLog += "\n" + validationLog.str();
    363 
    364 		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
    365 	}
    366 }
    367 
    368 de::Mutex							cacheFileMutex;
    369 map<deUint32, vector<deUint32> >	cacheFileIndex;
    370 bool								cacheFileFirstRun = true;
    371 
    372 void shaderCacheFirstRunCheck (const char* shaderCacheFile, bool truncate)
    373 {
    374 	cacheFileMutex.lock();
    375 	if (cacheFileFirstRun)
    376 	{
    377 		cacheFileFirstRun = false;
    378 		if (truncate)
    379 		{
    380 			// Open file with "w" access to truncate it
    381 			FILE* f = fopen(shaderCacheFile, "wb");
    382 			if (f)
    383 				fclose(f);
    384 		}
    385 		else
    386 		{
    387 			// Parse chunked shader cache file for hashes and offsets
    388 			FILE* file = fopen(shaderCacheFile, "rb");
    389 			int count = 0;
    390 			if (file)
    391 			{
    392 				deUint32 chunksize	= 0;
    393 				deUint32 hash		= 0;
    394 				deUint32 offset		= 0;
    395 				bool ok				= true;
    396 				while (ok)
    397 				{
    398 					offset = (deUint32)ftell(file);
    399 					if (ok) ok = fread(&chunksize, 1, 4, file)				== 4;
    400 					if (ok) ok = fread(&hash, 1, 4, file)					== 4;
    401 					if (ok) cacheFileIndex[hash].push_back(offset);
    402 					if (ok) ok = fseek(file, offset + chunksize, SEEK_SET)	== 0;
    403 					count++;
    404 				}
    405 				fclose(file);
    406 			}
    407 		}
    408 	}
    409 	cacheFileMutex.unlock();
    410 }
    411 
    412 std::string intToString (deUint32 integer)
    413 {
    414 	std::stringstream temp_sstream;
    415 
    416 	temp_sstream << integer;
    417 
    418 	return temp_sstream.str();
    419 }
    420 
    421 vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename)
    422 {
    423 	deUint32		hash		= deStringHash(shaderstring.c_str());
    424 	deInt32			format;
    425 	deInt32			length;
    426 	deInt32			sourcelength;
    427 	deUint32		i;
    428 	deUint32		temp;
    429 	deUint8*		bin			= 0;
    430 	char*			source		= 0;
    431 	bool			ok			= true;
    432 	cacheFileMutex.lock();
    433 
    434 	if (cacheFileIndex.count(hash) == 0)
    435 	{
    436 		cacheFileMutex.unlock();
    437 		return 0;
    438 	}
    439 	FILE*			file		= fopen(shaderCacheFilename, "rb");
    440 	ok				= file											!= 0;
    441 
    442 	for (i = 0; i < cacheFileIndex[hash].size(); i++)
    443 	{
    444 		if (ok) ok = fseek(file, cacheFileIndex[hash][i], SEEK_SET)	== 0;
    445 		if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Chunk size (skip)
    446 		if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Stored hash
    447 		if (ok) ok = temp											== hash; // Double check
    448 		if (ok) ok = fread(&format, 1, 4, file)						== 4;
    449 		if (ok) ok = fread(&length, 1, 4, file)						== 4;
    450 		if (ok) ok = length											> 0; // sanity check
    451 		if (ok) bin = new deUint8[length];
    452 		if (ok) ok = fread(bin, 1, length, file)					== (size_t)length;
    453 		if (ok) ok = fread(&sourcelength, 1, 4, file)				== 4;
    454 		if (ok && sourcelength > 0)
    455 		{
    456 			source = new char[sourcelength + 1];
    457 			ok = fread(source, 1, sourcelength, file)				== (size_t)sourcelength;
    458 			source[sourcelength] = 0;
    459 		}
    460 		if (!ok || shaderstring != std::string(source))
    461 		{
    462 			// Mismatch, but may still exist in cache if there were hash collisions
    463 			delete[] source;
    464 			delete[] bin;
    465 		}
    466 		else
    467 		{
    468 			delete[] source;
    469 			if (file) fclose(file);
    470 			cacheFileMutex.unlock();
    471 			vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
    472 			delete[] bin;
    473 			return res;
    474 		}
    475 	}
    476 	if (file) fclose(file);
    477 	cacheFileMutex.unlock();
    478 	return 0;
    479 }
    480 
    481 void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename)
    482 {
    483 	if (binary == 0)
    484 		return;
    485 	deUint32			hash		= deStringHash(shaderstring.c_str());
    486 	deInt32				format		= binary->getFormat();
    487 	deUint32			length		= (deUint32)binary->getSize();
    488 	deUint32			chunksize;
    489 	deUint32			offset;
    490 	const deUint8*		bin			= binary->getBinary();
    491 	const de::FilePath	filePath	(shaderCacheFilename);
    492 
    493 	cacheFileMutex.lock();
    494 
    495 	if (!de::FilePath(filePath.getDirName()).exists())
    496 		de::createDirectoryAndParents(filePath.getDirName().c_str());
    497 
    498 	FILE*				file		= fopen(shaderCacheFilename, "ab");
    499 	if (!file)
    500 	{
    501 		cacheFileMutex.unlock();
    502 		return;
    503 	}
    504 	// Append mode starts writing from the end of the file,
    505 	// but unless we do a seek, ftell returns 0.
    506 	fseek(file, 0, SEEK_END);
    507 	offset		= (deUint32)ftell(file);
    508 	chunksize	= 4 + 4 + 4 + 4 + length + 4 + (deUint32)shaderstring.length();
    509 	fwrite(&chunksize, 1, 4, file);
    510 	fwrite(&hash, 1, 4, file);
    511 	fwrite(&format, 1, 4, file);
    512 	fwrite(&length, 1, 4, file);
    513 	fwrite(bin, 1, length, file);
    514 	length = (deUint32)shaderstring.length();
    515 	fwrite(&length, 1, 4, file);
    516 	fwrite(shaderstring.c_str(), 1, length, file);
    517 	fclose(file);
    518 	cacheFileIndex[hash].push_back(offset);
    519 
    520 	cacheFileMutex.unlock();
    521 }
    522 
    523 // Insert any information that may affect compilation into the shader string.
    524 void getCompileEnvironment (std::string& shaderstring)
    525 {
    526 	shaderstring += "GLSL:";
    527 	shaderstring += qpGetReleaseGlslName();
    528 	shaderstring += "\nSpir-v Tools:";
    529 	shaderstring += qpGetReleaseSpirvToolsName();
    530 	shaderstring += "\nSpir-v Headers:";
    531 	shaderstring += qpGetReleaseSpirvHeadersName();
    532 	shaderstring += "\n";
    533 }
    534 
    535 // Insert compilation options into the shader string.
    536 void getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
    537 {
    538 	shaderstring += "Target Spir-V ";
    539 	shaderstring += getSpirvVersionName(buildOptions.targetVersion);
    540 	shaderstring += "\n";
    541 	if (buildOptions.flags & ShaderBuildOptions::FLAG_ALLOW_RELAXED_OFFSETS)
    542 		shaderstring += "Flag:Allow relaxed offsets\n";
    543 	if (buildOptions.flags & ShaderBuildOptions::FLAG_USE_STORAGE_BUFFER_STORAGE_CLASS)
    544 		shaderstring += "Flag:Use storage buffer storage class\n";
    545 	if (optimizationRecipe != 0)
    546 	{
    547 		shaderstring += "Optimization recipe ";
    548 		shaderstring += optimizationRecipe;
    549 		shaderstring += "\n";
    550 	}
    551 }
    552 
    553 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
    554 {
    555 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
    556 	const bool			validateBinary		= VALIDATE_BINARIES;
    557 	vector<deUint32>	binary;
    558 	std::string			cachekey;
    559 	std::string			shaderstring;
    560 	vk::ProgramBinary*	res					= 0;
    561 	const int			optimizationRecipe	= commandLine.getOptimizationRecipe();
    562 
    563 	if (commandLine.isShadercacheEnabled())
    564 	{
    565 		shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
    566 		getCompileEnvironment(cachekey);
    567 		getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
    568 
    569 		for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
    570 		{
    571 			if (!program.sources[i].empty())
    572 			{
    573 				cachekey += glu::getShaderTypeName((glu::ShaderType)i);
    574 
    575 				for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
    576 					shaderstring += *it;
    577 			}
    578 		}
    579 
    580 		cachekey = cachekey + shaderstring;
    581 
    582 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
    583 
    584 		if (res)
    585 		{
    586 			buildInfo->program.infoLog		= "Loaded from cache";
    587 			buildInfo->program.linkOk		= true;
    588 			buildInfo->program.linkTimeUs	= 0;
    589 
    590 			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
    591 			{
    592 				if (!program.sources[shaderType].empty())
    593 				{
    594 					glu::ShaderInfo	shaderBuildInfo;
    595 
    596 					shaderBuildInfo.type			= (glu::ShaderType)shaderType;
    597 					shaderBuildInfo.source			= shaderstring;
    598 					shaderBuildInfo.compileTimeUs	= 0;
    599 					shaderBuildInfo.compileOk		= true;
    600 
    601 					buildInfo->shaders.push_back(shaderBuildInfo);
    602 				}
    603 			}
    604 		}
    605 	}
    606 
    607 	if (!res)
    608 	{
    609 		{
    610 			vector<deUint32> nonStrippedBinary;
    611 
    612 			if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
    613 				TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
    614 
    615 			TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
    616 			stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
    617 			TCU_CHECK_INTERNAL(!binary.empty());
    618 		}
    619 
    620 		if (optimizationRecipe != 0)
    621 		{
    622 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
    623 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
    624 		}
    625 
    626 		if (validateBinary)
    627 		{
    628 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
    629 		}
    630 
    631 		res = createProgramBinaryFromSpirV(binary);
    632 		if (commandLine.isShadercacheEnabled())
    633 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
    634 	}
    635 	return res;
    636 }
    637 
    638 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
    639 {
    640 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
    641 	const bool			validateBinary		= VALIDATE_BINARIES;
    642 	vector<deUint32>	binary;
    643 	std::string			cachekey;
    644 	std::string			shaderstring;
    645 	vk::ProgramBinary*	res					= 0;
    646 	const int			optimizationRecipe	= commandLine.getOptimizationRecipe();
    647 
    648 	if (commandLine.isShadercacheEnabled())
    649 	{
    650 		shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
    651 		getCompileEnvironment(cachekey);
    652 		getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
    653 
    654 		for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
    655 		{
    656 			if (!program.sources[i].empty())
    657 			{
    658 				cachekey += glu::getShaderTypeName((glu::ShaderType)i);
    659 
    660 				for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
    661 					shaderstring += *it;
    662 			}
    663 		}
    664 
    665 		cachekey = cachekey + shaderstring;
    666 
    667 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
    668 
    669 		if (res)
    670 		{
    671 			buildInfo->program.infoLog		= "Loaded from cache";
    672 			buildInfo->program.linkOk		= true;
    673 			buildInfo->program.linkTimeUs	= 0;
    674 
    675 			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
    676 			{
    677 				if (!program.sources[shaderType].empty())
    678 				{
    679 					glu::ShaderInfo	shaderBuildInfo;
    680 
    681 					shaderBuildInfo.type			= (glu::ShaderType)shaderType;
    682 					shaderBuildInfo.source			= shaderstring;
    683 					shaderBuildInfo.compileTimeUs	= 0;
    684 					shaderBuildInfo.compileOk		= true;
    685 
    686 					buildInfo->shaders.push_back(shaderBuildInfo);
    687 				}
    688 			}
    689 		}
    690 	}
    691 
    692 	if (!res)
    693 	{
    694 		{
    695 			vector<deUint32> nonStrippedBinary;
    696 
    697 			if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
    698 				TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
    699 
    700 			TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
    701 			stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
    702 			TCU_CHECK_INTERNAL(!binary.empty());
    703 		}
    704 
    705 		if (optimizationRecipe != 0)
    706 		{
    707 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
    708 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
    709 		}
    710 
    711 		if (validateBinary)
    712 		{
    713 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
    714 		}
    715 
    716 		res = createProgramBinaryFromSpirV(binary);
    717 		if (commandLine.isShadercacheEnabled())
    718 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
    719 	}
    720 	return res;
    721 }
    722 
    723 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
    724 {
    725 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
    726 	const bool			validateBinary		= VALIDATE_BINARIES;
    727 	vector<deUint32>	binary;
    728 	vk::ProgramBinary*	res					= 0;
    729 	std::string			cachekey;
    730 	const int			optimizationRecipe	= commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
    731 
    732 	if (commandLine.isShadercacheEnabled())
    733 	{
    734 		shaderCacheFirstRunCheck(commandLine.getShaderCacheFilename(), commandLine.isShaderCacheTruncateEnabled());
    735 		getCompileEnvironment(cachekey);
    736 		cachekey += "Target Spir-V ";
    737 		cachekey += getSpirvVersionName(spirvVersion);
    738 		cachekey += "\n";
    739 		if (optimizationRecipe != 0)
    740 		{
    741 			cachekey += "Optimization recipe ";
    742 			cachekey += optimizationRecipe;
    743 			cachekey += "\n";
    744 		}
    745 
    746 		cachekey += program.source;
    747 
    748 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename());
    749 
    750 		if (res)
    751 		{
    752 			buildInfo->source			= program.source;
    753 			buildInfo->compileOk		= true;
    754 			buildInfo->compileTimeUs	= 0;
    755 			buildInfo->infoLog			= "Loaded from cache";
    756 		}
    757 	}
    758 
    759 	if (!res)
    760 	{
    761 
    762 		if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
    763 			TCU_THROW(InternalError, "Failed to assemble SPIR-V");
    764 
    765 		if (optimizationRecipe != 0)
    766 		{
    767 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
    768 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
    769 		}
    770 
    771 		if (validateBinary)
    772 		{
    773 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
    774 		}
    775 
    776 		res = createProgramBinaryFromSpirV(binary);
    777 		if (commandLine.isShadercacheEnabled())
    778 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename());
    779 	}
    780 	return res;
    781 }
    782 
    783 #else // !DEQP_HAVE_SPIRV_TOOLS
    784 
    785 ProgramBinary* buildProgram (const GlslSource&, glu::ShaderProgramInfo*, const tcu::CommandLine&)
    786 {
    787 	TCU_THROW(NotSupportedError, "GLSL to SPIR-V compilation not supported (DEQP_HAVE_GLSLANG not defined)");
    788 }
    789 
    790 ProgramBinary* buildProgram (const HlslSource&, glu::ShaderProgramInfo*, const tcu::CommandLine&)
    791 {
    792 	TCU_THROW(NotSupportedError, "HLSL to SPIR-V compilation not supported (DEQP_HAVE_GLSLANG not defined)");
    793 }
    794 
    795 ProgramBinary* assembleProgram (const SpirVAsmSource&, SpirVProgramInfo*, const tcu::CommandLine&)
    796 {
    797 	TCU_THROW(NotSupportedError, "SPIR-V assembly not supported (DEQP_HAVE_SPIRV_TOOLS not defined)");
    798 }
    799 #endif
    800 
    801 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
    802 {
    803 	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
    804 	{
    805 		TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
    806 
    807 		if (isNativeSpirVBinaryEndianness())
    808 			disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
    809 							 extractSpirvVersion(program));
    810 		else
    811 			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
    812 	}
    813 	else
    814 		TCU_THROW(NotSupportedError, "Unsupported program format");
    815 }
    816 
    817 bool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
    818 {
    819 	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
    820 	{
    821 		if (!isSaneSpirVBinary(program))
    822 		{
    823 			*dst << "Binary doesn't look like SPIR-V at all";
    824 			return false;
    825 		}
    826 
    827 		if (isNativeSpirVBinaryEndianness())
    828 			return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
    829 		else
    830 			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
    831 	}
    832 	else
    833 		TCU_THROW(NotSupportedError, "Unsupported program format");
    834 }
    835 
    836 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
    837 {
    838 	if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
    839 	{
    840 		const struct VkShaderModuleCreateInfo		shaderModuleInfo	=
    841 		{
    842 			VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
    843 			DE_NULL,
    844 			flags,
    845 			(deUintptr)binary.getSize(),
    846 			(const deUint32*)binary.getBinary(),
    847 		};
    848 
    849 		return createShaderModule(deviceInterface, device, &shaderModuleInfo);
    850 	}
    851 	else
    852 		TCU_THROW(NotSupportedError, "Unsupported program format");
    853 }
    854 
    855 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
    856 {
    857 	switch (shaderStage)
    858 	{
    859 		case VK_SHADER_STAGE_VERTEX_BIT:					return glu::SHADERTYPE_VERTEX;
    860 		case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:		return glu::SHADERTYPE_TESSELLATION_CONTROL;
    861 		case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:	return glu::SHADERTYPE_TESSELLATION_EVALUATION;
    862 		case VK_SHADER_STAGE_GEOMETRY_BIT:					return glu::SHADERTYPE_GEOMETRY;
    863 		case VK_SHADER_STAGE_FRAGMENT_BIT:					return glu::SHADERTYPE_FRAGMENT;
    864 		case VK_SHADER_STAGE_COMPUTE_BIT:					return glu::SHADERTYPE_COMPUTE;
    865 		default:
    866 			DE_FATAL("Unknown shader stage");
    867 			return glu::SHADERTYPE_LAST;
    868 	}
    869 }
    870 
    871 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
    872 {
    873 	static const VkShaderStageFlagBits s_shaderStages[] =
    874 	{
    875 		VK_SHADER_STAGE_VERTEX_BIT,
    876 		VK_SHADER_STAGE_FRAGMENT_BIT,
    877 		VK_SHADER_STAGE_GEOMETRY_BIT,
    878 		VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
    879 		VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
    880 		VK_SHADER_STAGE_COMPUTE_BIT
    881 	};
    882 
    883 	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
    884 }
    885 
    886 // Baseline version, to be used for shaders which don't specify a version
    887 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
    888 {
    889 	return vk::SPIRV_VERSION_1_0;
    890 }
    891 
    892 // Max supported versions for each vulkan version
    893 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
    894 {
    895 	vk::SpirvVersion	result			= vk::SPIRV_VERSION_LAST;
    896 
    897 	deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
    898 	if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
    899 		result = vk::SPIRV_VERSION_1_0;
    900 	else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
    901 		result = vk::SPIRV_VERSION_1_3;
    902 
    903 	DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
    904 
    905 	return result;
    906 }
    907 
    908 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
    909 {
    910 	vk::SpirvVersion	result			= vk::SPIRV_VERSION_LAST;
    911 
    912 	deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
    913 	if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
    914 		result = vk::SPIRV_VERSION_1_0;
    915 	else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
    916 		result = vk::SPIRV_VERSION_1_3;
    917 
    918 	DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
    919 
    920 	return result;
    921 }
    922 
    923 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
    924 {
    925 	DE_STATIC_ASSERT(SPIRV_VERSION_1_3 + 1 == SPIRV_VERSION_LAST);
    926 
    927 	if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
    928 		TCU_THROW(InternalError, "Binary is not in SPIR-V format");
    929 
    930 	if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
    931 		TCU_THROW(InternalError, "Invalid SPIR-V header format");
    932 
    933 	const deUint32				spirvBinaryVersion10	= 0x00010000;
    934 	const deUint32				spirvBinaryVersion11	= 0x00010100;
    935 	const deUint32				spirvBinaryVersion12	= 0x00010200;
    936 	const deUint32				spirvBinaryVersion13	= 0x00010300;
    937 	const SpirvBinaryHeader*	header					= reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
    938 	const deUint32				spirvVersion			= isNativeSpirVBinaryEndianness()
    939 														? header->version
    940 														: deReverseBytes32(header->version);
    941 	SpirvVersion				result					= SPIRV_VERSION_LAST;
    942 
    943 	switch (spirvVersion)
    944 	{
    945 		case spirvBinaryVersion10:	result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
    946 		case spirvBinaryVersion11:	result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
    947 		case spirvBinaryVersion12:	result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
    948 		case spirvBinaryVersion13:	result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
    949 		default:					TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
    950 	}
    951 
    952 	return result;
    953 }
    954 
    955 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
    956 {
    957 	DE_STATIC_ASSERT(SPIRV_VERSION_1_3 + 1 == SPIRV_VERSION_LAST);
    958 	DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
    959 
    960 	std::string result;
    961 
    962 	switch (spirvVersion)
    963 	{
    964 		case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
    965 		case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
    966 		case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
    967 		case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
    968 		default:				result = "Unknown";
    969 	}
    970 
    971 	return result;
    972 }
    973 
    974 SpirvVersion& operator++(SpirvVersion& spirvVersion)
    975 {
    976 	if (spirvVersion == SPIRV_VERSION_LAST)
    977 		spirvVersion = SPIRV_VERSION_1_0;
    978 	else
    979 		spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
    980 
    981 	return spirvVersion;
    982 }
    983 
    984 } // vk
    985