• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ProgramBinary(ProgramFormat format,size_t binarySize,const deUint8 * binary)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 
isNativeSpirVBinaryEndianness(void)73 bool isNativeSpirVBinaryEndianness (void)
74 {
75 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
76 	return true;
77 #else
78 	return false;
79 #endif
80 }
81 
isSaneSpirVBinary(const ProgramBinary & binary)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 
optimizeCompiledBinary(vector<deUint32> & binary,int optimizationRecipe,const SpirvVersion spirvVersion)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 
createProgramBinaryFromSpirV(const vector<deUint32> & binary)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 
validateCompiledBinary(const vector<deUint32> & binary,glu::ShaderProgramInfo * buildInfo,const SpirvValidatorOptions & options)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 
validateCompiledBinary(const vector<deUint32> & binary,SpirVProgramInfo * buildInfo,const SpirvValidatorOptions & options)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 
shaderCacheFirstRunCheck(const char * shaderCacheFile,bool truncate)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 
intToString(deUint32 integer)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 
shadercacheLoad(const std::string & shaderstring,const char * shaderCacheFilename)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 
shadercacheSave(const vk::ProgramBinary * binary,const std::string & shaderstring,const char * shaderCacheFilename)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.
getCompileEnvironment(std::string & shaderstring)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.
getBuildOptions(std::string & shaderstring,const ShaderBuildOptions & buildOptions,int optimizationRecipe)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 
buildProgram(const GlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)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 
buildProgram(const HlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)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 
assembleProgram(const SpirVAsmSource & program,SpirVProgramInfo * buildInfo,const tcu::CommandLine & commandLine)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 
buildProgram(const GlslSource &,glu::ShaderProgramInfo *,const tcu::CommandLine &)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 
buildProgram(const HlslSource &,glu::ShaderProgramInfo *,const tcu::CommandLine &)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 
assembleProgram(const SpirVAsmSource &,SpirVProgramInfo *,const tcu::CommandLine &)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 
disassembleProgram(const ProgramBinary & program,std::ostream * dst)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 
validateProgram(const ProgramBinary & program,std::ostream * dst,const SpirvValidatorOptions & options)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 
createShaderModule(const DeviceInterface & deviceInterface,VkDevice device,const ProgramBinary & binary,VkShaderModuleCreateFlags flags)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 
getGluShaderType(VkShaderStageFlagBits shaderStage)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 
getVkShaderStage(glu::ShaderType shaderType)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
getBaselineSpirvVersion(const deUint32)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
getMaxSpirvVersionForAsm(const deUint32 vulkanVersion)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 
getMaxSpirvVersionForGlsl(const deUint32 vulkanVersion)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 
extractSpirvVersion(const ProgramBinary & binary)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 
getSpirvVersionName(const SpirvVersion spirvVersion)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 
operator ++(SpirvVersion & spirvVersion)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