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