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