• 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 #if DE_OS == DE_OS_ANDROID
44 #define DISABLE_SHADERCACHE_IPC
45 #endif
46 
47 namespace vk
48 {
49 
50 using std::string;
51 using std::vector;
52 using std::map;
53 
54 #if defined(DE_DEBUG)
55 #	define VALIDATE_BINARIES	true
56 #else
57 #	define VALIDATE_BINARIES	false
58 #endif
59 
60 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
61 
62 // ProgramBinary
63 
ProgramBinary(ProgramFormat format,size_t binarySize,const deUint8 * binary)64 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
65 	: m_format	(format)
66 	, m_binary	(binary, binary+binarySize)
67 	, m_used	(false)
68 {
69 }
70 
71 // Utils
72 
73 namespace
74 {
75 
isNativeSpirVBinaryEndianness(void)76 bool isNativeSpirVBinaryEndianness (void)
77 {
78 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
79 	return true;
80 #else
81 	return false;
82 #endif
83 }
84 
isSaneSpirVBinary(const ProgramBinary & binary)85 bool isSaneSpirVBinary (const ProgramBinary& binary)
86 {
87 	const deUint32	spirvMagicWord	= 0x07230203;
88 	const deUint32	spirvMagicBytes	= isNativeSpirVBinaryEndianness()
89 									? spirvMagicWord
90 									: deReverseBytes32(spirvMagicWord);
91 
92 	DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
93 
94 	if (binary.getSize() % sizeof(deUint32) != 0)
95 		return false;
96 
97 	if (binary.getSize() < sizeof(deUint32))
98 		return false;
99 
100 	if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
101 		return false;
102 
103 	return true;
104 }
105 
optimizeCompiledBinary(vector<deUint32> & binary,int optimizationRecipe,const SpirvVersion spirvVersion)106 void optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
107 {
108 	spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
109 
110 	// Map SpirvVersion with spv_target_env:
111 	switch (spirvVersion)
112 	{
113 		case SPIRV_VERSION_1_0: targetEnv = SPV_ENV_VULKAN_1_0;	break;
114 		case SPIRV_VERSION_1_1:
115 		case SPIRV_VERSION_1_2:
116 		case SPIRV_VERSION_1_3: targetEnv = SPV_ENV_VULKAN_1_1;	break;
117 		case SPIRV_VERSION_1_4: targetEnv = SPV_ENV_VULKAN_1_1_SPIRV_1_4;	break;
118 		case SPIRV_VERSION_1_5: targetEnv = SPV_ENV_VULKAN_1_2;	break;
119 		case SPIRV_VERSION_1_6: targetEnv = SPV_ENV_VULKAN_1_3;	break;
120 		default:
121 			TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
122 	}
123 
124 	spvtools::Optimizer optimizer(targetEnv);
125 
126 	switch (optimizationRecipe)
127 	{
128 		case 1:
129 			optimizer.RegisterPerformancePasses();
130 			break;
131 		case 2:
132 			optimizer.RegisterSizePasses();
133 			break;
134 		default:
135 			TCU_THROW(InternalError, "Unknown optimization recipe requested");
136 	}
137 
138 	spvtools::OptimizerOptions optimizer_options;
139 	optimizer_options.set_run_validator(false);
140 	const bool ok = optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
141 
142 	if (!ok)
143 		TCU_THROW(InternalError, "Optimizer call failed");
144 }
145 
createProgramBinaryFromSpirV(const vector<deUint32> & binary)146 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
147 {
148 	DE_ASSERT(!binary.empty());
149 
150 	if (isNativeSpirVBinaryEndianness())
151 		return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
152 	else
153 		TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
154 }
155 
156 } // anonymous
157 
validateCompiledBinary(const vector<deUint32> & binary,glu::ShaderProgramInfo * buildInfo,const SpirvValidatorOptions & options)158 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
159 {
160 	std::ostringstream validationLog;
161 
162 	if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
163 	{
164 		buildInfo->program.linkOk	 = false;
165 		buildInfo->program.infoLog	+= "\n" + validationLog.str();
166 
167 		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
168 	}
169 }
170 
validateCompiledBinary(const vector<deUint32> & binary,SpirVProgramInfo * buildInfo,const SpirvValidatorOptions & options)171 void validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
172 {
173 	std::ostringstream validationLog;
174 
175 	if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
176 	{
177 		buildInfo->compileOk = false;
178 		buildInfo->infoLog += "\n" + validationLog.str();
179 
180 		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
181 	}
182 }
183 
184 // IPC functions
185 #ifndef DISABLE_SHADERCACHE_IPC
186 #include "vkIPC.inl"
187 #endif
188 
189 // Overridable wrapper for de::Mutex
190 class cacheMutex
191 {
192 public:
cacheMutex()193 	cacheMutex () {}
~cacheMutex()194 	virtual ~cacheMutex () {}
lock()195 	virtual void lock () { localMutex.lock(); }
unlock()196 	virtual void unlock () { localMutex.unlock(); }
197 private:
198 	de::Mutex localMutex;
199 };
200 
201 #ifndef DISABLE_SHADERCACHE_IPC
202 // Overriden cacheMutex that uses IPC instead
203 class cacheMutexIPC : public cacheMutex
204 {
205 public:
cacheMutexIPC()206 	cacheMutexIPC ()
207 	{
208 		ipc_sem_init(&guard,(char*)"cts_shadercache_ipc_guard");
209 		ipc_sem_create(&guard, 1);
210 	}
~cacheMutexIPC()211 	virtual ~cacheMutexIPC ()
212 	{
213 		ipc_sem_close(&guard);
214 	}
lock()215 	virtual void lock () { ipc_sem_decrement(&guard); }
unlock()216 	virtual void unlock () { ipc_sem_increment(&guard); }
217 private:
218 	ipc_sharedsemaphore guard;
219 };
220 #endif
221 
222 // Each cache node takes 4 * 4 = 16 bytes; 1M items takes 16M memory.
223 const deUint32						cacheMaxItems		= 1024 * 1024;
224 cacheMutex*							cacheFileMutex		= 0;
225 bool								cacheFileFirstRun	= true;
226 deUint32*							cacheMempool		= 0;
227 #ifndef DISABLE_SHADERCACHE_IPC
228 ipc_sharedmemory					cacheIPCMemory;
229 #endif
230 
231 struct cacheNode
232 {
233 	deUint32 key;
234 	deUint32 data;
235 	deUint32 right_child;
236 	deUint32 left_child;
237 };
238 
cacheSearch(deUint32 key)239 cacheNode* cacheSearch (deUint32 key)
240 {
241 	cacheNode*		r		= (cacheNode*)(cacheMempool + 1);
242 	int*			tail	= (int*)cacheMempool;
243 	unsigned int	p		= 0;
244 
245 	if (!*tail) {
246 		// Cache is empty.
247 		return 0;
248 	}
249 
250 	while (1)
251 	{
252 		if (r[p].key == key)
253 			return &r[p];
254 
255 		if (key > r[p].key)
256 			p = r[p].right_child;
257 		else
258 			p = r[p].left_child;
259 
260 		if (p == 0)
261 			return 0;
262 	}
263 }
264 
cacheInsert(deUint32 key,deUint32 data)265 void cacheInsert (deUint32 key, deUint32 data)
266 {
267 	cacheNode*	r		= (cacheNode*)(cacheMempool + 1);
268 	int*		tail	= (int*)cacheMempool;
269 	int			newnode	= *tail;
270 
271 	DE_ASSERT(newnode < cacheMaxItems);
272 
273 	// If we run out of cache space, reset the cache index.
274 	if (newnode >= cacheMaxItems)
275 	{
276 		*tail = 0;
277 		newnode = 0;
278 	}
279 
280 	r[*tail].data = data;
281 	r[*tail].key = key;
282 	r[*tail].left_child = 0;
283 	r[*tail].right_child = 0;
284 
285 	(*tail)++;
286 
287 	if (newnode == 0)
288 	{
289 		// first
290 		return;
291 	}
292 
293 	int p = 0;
294 	while (1)
295 	{
296 		if (r[p].key == key)
297 		{
298 			// collision; use the latest data
299 			r[p].data = data;
300 			(*tail)--;
301 			return;
302 		}
303 
304 		if (key > r[p].key)
305 		{
306 			if (r[p].right_child != 0)
307 			{
308 				p = r[p].right_child;
309 			}
310 			else
311 			{
312 				r[p].right_child = newnode;
313 				return;
314 			}
315 		}
316 		else
317 		{
318 			if (r[p].left_child != 0)
319 			{
320 				p = r[p].left_child;
321 			}
322 			else
323 			{
324 				r[p].left_child = newnode;
325 				return;
326 			}
327 		}
328 	}
329 }
330 
331 // Called via atexit()
shaderCacheClean()332 void shaderCacheClean ()
333 {
334 	delete cacheFileMutex;
335 	delete[] cacheMempool;
336 }
337 
338 #ifndef DISABLE_SHADERCACHE_IPC
339 // Called via atexit()
shaderCacheCleanIPC()340 void shaderCacheCleanIPC ()
341 {
342 	delete cacheFileMutex;
343 	ipc_mem_close(&cacheIPCMemory);
344 }
345 #endif
346 
shaderCacheFirstRunCheck(const tcu::CommandLine & commandLine)347 void shaderCacheFirstRunCheck (const tcu::CommandLine& commandLine)
348 {
349 	bool first = true;
350 
351 	if (!cacheFileFirstRun)
352 		return;
353 
354 	cacheFileFirstRun = false;
355 
356 #ifndef DISABLE_SHADERCACHE_IPC
357 	if (commandLine.isShaderCacheIPCEnabled())
358 	{
359 		// IPC path, allocate shared mutex and shared memory
360 		cacheFileMutex = new cacheMutexIPC;
361 		cacheFileMutex->lock();
362 		ipc_mem_init(&cacheIPCMemory,(char*)"cts_shadercache_memory", sizeof(deUint32) * (cacheMaxItems * 4 + 1));
363 		if (ipc_mem_open_existing(&cacheIPCMemory) != 0)
364 		{
365 			ipc_mem_create(&cacheIPCMemory);
366 			cacheMempool = (deUint32*)ipc_mem_access(&cacheIPCMemory);
367 			cacheMempool[0] = 0;
368 		}
369 		else
370 		{
371 			cacheMempool = (deUint32*)ipc_mem_access(&cacheIPCMemory);
372 			first = false;
373 		}
374 		atexit(shaderCacheCleanIPC);
375 	}
376 	else
377 #endif
378 	{
379 		// Non-IPC path, allocate local mutex and memory
380 		cacheFileMutex = new cacheMutex;
381 		cacheFileMutex->lock();
382 		cacheMempool = new deUint32[cacheMaxItems * 4 + 1];
383 		cacheMempool[0] = 0;
384 
385 		atexit(shaderCacheClean);
386 	}
387 
388 	if (first)
389 	{
390 		if (commandLine.isShaderCacheTruncateEnabled())
391 		{
392 			// Open file with "w" access to truncate it
393 			FILE* f = fopen(commandLine.getShaderCacheFilename(), "wb");
394 			if (f)
395 				fclose(f);
396 		}
397 		else
398 		{
399 			// Parse chunked shader cache file for hashes and offsets
400 			FILE* file	= fopen(commandLine.getShaderCacheFilename(), "rb");
401 			int count	= 0;
402 			if (file)
403 			{
404 				deUint32 chunksize	= 0;
405 				deUint32 hash		= 0;
406 				deUint32 offset		= 0;
407 				bool ok				= true;
408 				while (ok)
409 				{
410 					offset = (deUint32)ftell(file);
411 					if (ok) ok = fread(&chunksize, 1, 4, file)				== 4;
412 					if (ok) ok = fread(&hash, 1, 4, file)					== 4;
413 					if (ok) cacheInsert(hash, offset);
414 					if (ok) ok = fseek(file, offset + chunksize, SEEK_SET)	== 0;
415 					count++;
416 				}
417 				fclose(file);
418 			}
419 		}
420 	}
421 	cacheFileMutex->unlock();
422 }
423 
intToString(deUint32 integer)424 std::string intToString (deUint32 integer)
425 {
426 	std::stringstream temp_sstream;
427 
428 	temp_sstream << integer;
429 
430 	return temp_sstream.str();
431 }
432 
433 // 32-bit FNV-1 hash
shadercacheHash(const char * str)434 deUint32 shadercacheHash (const char* str)
435 {
436 	deUint32 hash = 0x811c9dc5;
437 	deUint32 c;
438 	while ((c = (deUint32)*str++) != 0)
439 	{
440 		hash *= 16777619;
441 		hash ^= c;
442 	}
443 	return hash;
444 }
445 
shadercacheLoad(const std::string & shaderstring,const char * shaderCacheFilename,deUint32 hash)446 vk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename, deUint32 hash)
447 {
448 	deInt32			format;
449 	deInt32			length;
450 	deInt32			sourcelength;
451 	deUint32		temp;
452 	deUint8*		bin			= 0;
453 	char*			source		= 0;
454 	deBool			ok			= true;
455 	deBool			diff		= true;
456 	cacheNode*		node		= 0;
457 	cacheFileMutex->lock();
458 
459 	node = cacheSearch(hash);
460 	if (node == 0)
461 	{
462 		cacheFileMutex->unlock();
463 		return 0;
464 	}
465 	FILE*			file		= fopen(shaderCacheFilename, "rb");
466 	ok				= file										!= 0;
467 
468 	if (ok) ok = fseek(file, node->data, SEEK_SET)				== 0;
469 	if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Chunk size (skip)
470 	if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Stored hash
471 	if (ok) ok = temp											== hash; // Double check
472 	if (ok) ok = fread(&format, 1, 4, file)						== 4;
473 	if (ok) ok = fread(&length, 1, 4, file)						== 4;
474 	if (ok) ok = length											> 0; // sanity check
475 	if (ok) bin = new deUint8[length];
476 	if (ok) ok = fread(bin, 1, length, file)					== (size_t)length;
477 	if (ok) ok = fread(&sourcelength, 1, 4, file)				== 4;
478 	if (ok && sourcelength > 0)
479 	{
480 		source = new char[sourcelength + 1];
481 		ok = fread(source, 1, sourcelength, file)				== (size_t)sourcelength;
482 		source[sourcelength] = 0;
483 		diff = shaderstring != std::string(source);
484 	}
485 	if (!ok || diff)
486 	{
487 		// Mismatch
488 		delete[] source;
489 		delete[] bin;
490 	}
491 	else
492 	{
493 		delete[] source;
494 		if (file) fclose(file);
495 		cacheFileMutex->unlock();
496 		vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
497 		delete[] bin;
498 		return res;
499 	}
500 	if (file) fclose(file);
501 	cacheFileMutex->unlock();
502 	return 0;
503 }
504 
shadercacheSave(const vk::ProgramBinary * binary,const std::string & shaderstring,const char * shaderCacheFilename,deUint32 hash)505 void shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename, deUint32 hash)
506 {
507 	if (binary == 0)
508 		return;
509 	deInt32				format		= binary->getFormat();
510 	deUint32			length		= (deUint32)binary->getSize();
511 	deUint32			chunksize;
512 	deUint32			offset;
513 	const deUint8*		bin			= binary->getBinary();
514 	const de::FilePath	filePath	(shaderCacheFilename);
515 	cacheNode*			node		= 0;
516 
517 	cacheFileMutex->lock();
518 
519 	node = cacheSearch(hash);
520 
521 	if (node)
522 	{
523 		FILE*			file		= fopen(shaderCacheFilename, "rb");
524 		deBool			ok			= (file != 0);
525 		deBool			diff		= DE_TRUE;
526 		deInt32			sourcelength;
527 		deUint32		temp;
528 
529 		deUint32	cachedLength	= 0;
530 
531 		if (ok) ok = fseek(file, node->data, SEEK_SET)				== 0;
532 		if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Chunk size (skip)
533 		if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Stored hash
534 		if (ok) ok = temp											== hash; // Double check
535 		if (ok) ok = fread(&temp, 1, 4, file)						== 4;
536 		if (ok) ok = fread(&cachedLength, 1, 4, file)				== 4;
537 		if (ok) ok = cachedLength									> 0; // sanity check
538 		if (ok) fseek(file, cachedLength, SEEK_CUR); // skip binary
539 		if (ok) ok = fread(&sourcelength, 1, 4, file)				== 4;
540 
541 		if (ok && sourcelength > 0)
542 		{
543 			char* source;
544 			source	= new char[sourcelength + 1];
545 			ok		= fread(source, 1, sourcelength, file)			== (size_t)sourcelength;
546 			source[sourcelength] = 0;
547 			diff	= shaderstring != std::string(source);
548 			delete[] source;
549 		}
550 
551 		if (ok && !diff)
552 		{
553 			// Already in cache (written by another thread, probably)
554 			fclose(file);
555 			cacheFileMutex->unlock();
556 			return;
557 		}
558 		fclose(file);
559 	}
560 
561 	if (!de::FilePath(filePath.getDirName()).exists())
562 		de::createDirectoryAndParents(filePath.getDirName().c_str());
563 
564 	FILE*				file		= fopen(shaderCacheFilename, "ab");
565 	if (!file)
566 	{
567 		cacheFileMutex->unlock();
568 		return;
569 	}
570 	// Append mode starts writing from the end of the file,
571 	// but unless we do a seek, ftell returns 0.
572 	fseek(file, 0, SEEK_END);
573 	offset		= (deUint32)ftell(file);
574 	chunksize	= 4 + 4 + 4 + 4 + length + 4 + (deUint32)shaderstring.length();
575 	fwrite(&chunksize, 1, 4, file);
576 	fwrite(&hash, 1, 4, file);
577 	fwrite(&format, 1, 4, file);
578 	fwrite(&length, 1, 4, file);
579 	fwrite(bin, 1, length, file);
580 	length = (deUint32)shaderstring.length();
581 	fwrite(&length, 1, 4, file);
582 	fwrite(shaderstring.c_str(), 1, length, file);
583 	fclose(file);
584 	cacheInsert(hash, offset);
585 
586 	cacheFileMutex->unlock();
587 }
588 
589 // Insert any information that may affect compilation into the shader string.
getCompileEnvironment(std::string & shaderstring)590 void getCompileEnvironment (std::string& shaderstring)
591 {
592 	shaderstring += "GLSL:";
593 	shaderstring += qpGetReleaseGlslName();
594 	shaderstring += "\nSpir-v Tools:";
595 	shaderstring += qpGetReleaseSpirvToolsName();
596 	shaderstring += "\nSpir-v Headers:";
597 	shaderstring += qpGetReleaseSpirvHeadersName();
598 	shaderstring += "\n";
599 }
600 
601 // Insert compilation options into the shader string.
getBuildOptions(std::string & shaderstring,const ShaderBuildOptions & buildOptions,int optimizationRecipe)602 void getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
603 {
604 	shaderstring += "Target Spir-V ";
605 	shaderstring += getSpirvVersionName(buildOptions.targetVersion);
606 	shaderstring += "\n";
607 	if (buildOptions.flags & ShaderBuildOptions::FLAG_ALLOW_RELAXED_OFFSETS)
608 		shaderstring += "Flag:Allow relaxed offsets\n";
609 	if (buildOptions.flags & ShaderBuildOptions::FLAG_USE_STORAGE_BUFFER_STORAGE_CLASS)
610 		shaderstring += "Flag:Use storage buffer storage class\n";
611 	if (optimizationRecipe != 0)
612 	{
613 		shaderstring += "Optimization recipe ";
614 		shaderstring += de::toString(optimizationRecipe);
615 		shaderstring += "\n";
616 	}
617 }
618 
buildProgram(const GlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)619 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
620 {
621 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
622 	const bool			validateBinary		= VALIDATE_BINARIES;
623 	vector<deUint32>	binary;
624 	std::string			cachekey;
625 	std::string			shaderstring;
626 	vk::ProgramBinary*	res					= 0;
627 	const int			optimizationRecipe	= commandLine.getOptimizationRecipe();
628 	deUint32			hash				= 0;
629 
630 	if (commandLine.isShadercacheEnabled())
631 	{
632 		shaderCacheFirstRunCheck(commandLine);
633 		getCompileEnvironment(cachekey);
634 		getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
635 
636 		for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
637 		{
638 			if (!program.sources[i].empty())
639 			{
640 				cachekey += glu::getShaderTypeName((glu::ShaderType)i);
641 
642 				for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
643 					shaderstring += *it;
644 			}
645 		}
646 
647 		cachekey = cachekey + shaderstring;
648 
649 		hash = shadercacheHash(cachekey.c_str());
650 
651 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
652 
653 		if (res)
654 		{
655 			buildInfo->program.infoLog		= "Loaded from cache";
656 			buildInfo->program.linkOk		= true;
657 			buildInfo->program.linkTimeUs	= 0;
658 
659 			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
660 			{
661 				if (!program.sources[shaderType].empty())
662 				{
663 					glu::ShaderInfo	shaderBuildInfo;
664 
665 					shaderBuildInfo.type			= (glu::ShaderType)shaderType;
666 					shaderBuildInfo.source			= shaderstring;
667 					shaderBuildInfo.compileTimeUs	= 0;
668 					shaderBuildInfo.compileOk		= true;
669 
670 					buildInfo->shaders.push_back(shaderBuildInfo);
671 				}
672 			}
673 		}
674 	}
675 
676 	if (!res)
677 	{
678 		{
679 			vector<deUint32> nonStrippedBinary;
680 
681 			if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
682 				TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
683 
684 			TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
685 			stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
686 			TCU_CHECK_INTERNAL(!binary.empty());
687 		}
688 
689 		if (optimizationRecipe != 0)
690 		{
691 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
692 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
693 		}
694 
695 		if (validateBinary)
696 		{
697 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
698 		}
699 
700 		res = createProgramBinaryFromSpirV(binary);
701 		if (commandLine.isShadercacheEnabled())
702 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
703 	}
704 	return res;
705 }
706 
buildProgram(const HlslSource & program,glu::ShaderProgramInfo * buildInfo,const tcu::CommandLine & commandLine)707 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
708 {
709 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
710 	const bool			validateBinary		= VALIDATE_BINARIES;
711 	vector<deUint32>	binary;
712 	std::string			cachekey;
713 	std::string			shaderstring;
714 	vk::ProgramBinary*	res					= 0;
715 	const int			optimizationRecipe	= commandLine.getOptimizationRecipe();
716 	deInt32				hash				= 0;
717 
718 	if (commandLine.isShadercacheEnabled())
719 	{
720 		shaderCacheFirstRunCheck(commandLine);
721 		getCompileEnvironment(cachekey);
722 		getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
723 
724 		for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
725 		{
726 			if (!program.sources[i].empty())
727 			{
728 				cachekey += glu::getShaderTypeName((glu::ShaderType)i);
729 
730 				for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
731 					shaderstring += *it;
732 			}
733 		}
734 
735 		cachekey = cachekey + shaderstring;
736 
737 		hash = shadercacheHash(cachekey.c_str());
738 
739 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
740 
741 		if (res)
742 		{
743 			buildInfo->program.infoLog		= "Loaded from cache";
744 			buildInfo->program.linkOk		= true;
745 			buildInfo->program.linkTimeUs	= 0;
746 
747 			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
748 			{
749 				if (!program.sources[shaderType].empty())
750 				{
751 					glu::ShaderInfo	shaderBuildInfo;
752 
753 					shaderBuildInfo.type			= (glu::ShaderType)shaderType;
754 					shaderBuildInfo.source			= shaderstring;
755 					shaderBuildInfo.compileTimeUs	= 0;
756 					shaderBuildInfo.compileOk		= true;
757 
758 					buildInfo->shaders.push_back(shaderBuildInfo);
759 				}
760 			}
761 		}
762 	}
763 
764 	if (!res)
765 	{
766 		{
767 			vector<deUint32> nonStrippedBinary;
768 
769 			if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
770 				TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
771 
772 			TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
773 			stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
774 			TCU_CHECK_INTERNAL(!binary.empty());
775 		}
776 
777 		if (optimizationRecipe != 0)
778 		{
779 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
780 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
781 		}
782 
783 		if (validateBinary)
784 		{
785 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
786 		}
787 
788 		res = createProgramBinaryFromSpirV(binary);
789 		if (commandLine.isShadercacheEnabled())
790 		{
791 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
792 		}
793 	}
794 	return res;
795 }
796 
assembleProgram(const SpirVAsmSource & program,SpirVProgramInfo * buildInfo,const tcu::CommandLine & commandLine)797 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
798 {
799 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
800 	const bool			validateBinary		= VALIDATE_BINARIES;
801 	vector<deUint32>	binary;
802 	vk::ProgramBinary*	res					= 0;
803 	std::string			cachekey;
804 	const int			optimizationRecipe	= commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
805 	deUint32			hash				= 0;
806 
807 	if (commandLine.isShadercacheEnabled())
808 	{
809 		shaderCacheFirstRunCheck(commandLine);
810 		getCompileEnvironment(cachekey);
811 		cachekey += "Target Spir-V ";
812 		cachekey += getSpirvVersionName(spirvVersion);
813 		cachekey += "\n";
814 		if (optimizationRecipe != 0)
815 		{
816 			cachekey += "Optimization recipe ";
817 			cachekey += de::toString(optimizationRecipe);
818 			cachekey += "\n";
819 		}
820 
821 		cachekey += program.source;
822 
823 		hash = shadercacheHash(cachekey.c_str());
824 
825 		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
826 
827 		if (res)
828 		{
829 			buildInfo->source			= program.source;
830 			buildInfo->compileOk		= true;
831 			buildInfo->compileTimeUs	= 0;
832 			buildInfo->infoLog			= "Loaded from cache";
833 		}
834 	}
835 
836 	if (!res)
837 	{
838 
839 		if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
840 			TCU_THROW(InternalError, "Failed to assemble SPIR-V");
841 
842 		if (optimizationRecipe != 0)
843 		{
844 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
845 			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
846 		}
847 
848 		if (validateBinary)
849 		{
850 			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
851 		}
852 
853 		res = createProgramBinaryFromSpirV(binary);
854 		if (commandLine.isShadercacheEnabled())
855 		{
856 			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
857 		}
858 	}
859 	return res;
860 }
861 
disassembleProgram(const ProgramBinary & program,std::ostream * dst)862 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
863 {
864 	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
865 	{
866 		TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
867 
868 		if (isNativeSpirVBinaryEndianness())
869 			disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
870 							 extractSpirvVersion(program));
871 		else
872 			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
873 	}
874 	else
875 		TCU_THROW(NotSupportedError, "Unsupported program format");
876 }
877 
validateProgram(const ProgramBinary & program,std::ostream * dst,const SpirvValidatorOptions & options)878 bool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
879 {
880 	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
881 	{
882 		if (!isSaneSpirVBinary(program))
883 		{
884 			*dst << "Binary doesn't look like SPIR-V at all";
885 			return false;
886 		}
887 
888 		if (isNativeSpirVBinaryEndianness())
889 			return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
890 		else
891 			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
892 	}
893 	else
894 		TCU_THROW(NotSupportedError, "Unsupported program format");
895 }
896 
createShaderModule(const DeviceInterface & deviceInterface,VkDevice device,const ProgramBinary & binary,VkShaderModuleCreateFlags flags)897 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
898 {
899 	if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
900 	{
901 		const struct VkShaderModuleCreateInfo		shaderModuleInfo	=
902 		{
903 			VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
904 			DE_NULL,
905 			flags,
906 			(deUintptr)binary.getSize(),
907 			(const deUint32*)binary.getBinary(),
908 		};
909 
910 		binary.setUsed();
911 
912 		return createShaderModule(deviceInterface, device, &shaderModuleInfo);
913 	}
914 	else
915 		TCU_THROW(NotSupportedError, "Unsupported program format");
916 }
917 
getGluShaderType(VkShaderStageFlagBits shaderStage)918 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
919 {
920 	switch (shaderStage)
921 	{
922 		case VK_SHADER_STAGE_VERTEX_BIT:					return glu::SHADERTYPE_VERTEX;
923 		case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:		return glu::SHADERTYPE_TESSELLATION_CONTROL;
924 		case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:	return glu::SHADERTYPE_TESSELLATION_EVALUATION;
925 		case VK_SHADER_STAGE_GEOMETRY_BIT:					return glu::SHADERTYPE_GEOMETRY;
926 		case VK_SHADER_STAGE_FRAGMENT_BIT:					return glu::SHADERTYPE_FRAGMENT;
927 		case VK_SHADER_STAGE_COMPUTE_BIT:					return glu::SHADERTYPE_COMPUTE;
928 		default:
929 			DE_FATAL("Unknown shader stage");
930 			return glu::SHADERTYPE_LAST;
931 	}
932 }
933 
getVkShaderStage(glu::ShaderType shaderType)934 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
935 {
936 	static const VkShaderStageFlagBits s_shaderStages[] =
937 	{
938 		VK_SHADER_STAGE_VERTEX_BIT,
939 		VK_SHADER_STAGE_FRAGMENT_BIT,
940 		VK_SHADER_STAGE_GEOMETRY_BIT,
941 		VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
942 		VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
943 		VK_SHADER_STAGE_COMPUTE_BIT,
944 #ifndef CTS_USES_VULKANSC
945 		VK_SHADER_STAGE_RAYGEN_BIT_NV,
946 		VK_SHADER_STAGE_ANY_HIT_BIT_NV,
947 		VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV,
948 		VK_SHADER_STAGE_MISS_BIT_NV,
949 		VK_SHADER_STAGE_INTERSECTION_BIT_NV,
950 		VK_SHADER_STAGE_CALLABLE_BIT_NV,
951 		VK_SHADER_STAGE_TASK_BIT_NV,
952 		VK_SHADER_STAGE_MESH_BIT_NV,
953 #else // CTS_USES_VULKANSC
954 		(VkShaderStageFlagBits)64u,
955 		(VkShaderStageFlagBits)128u,
956 		(VkShaderStageFlagBits)256u,
957 		(VkShaderStageFlagBits)512u,
958 		(VkShaderStageFlagBits)1024u,
959 		(VkShaderStageFlagBits)2048u,
960 		(VkShaderStageFlagBits)4096u,
961 		(VkShaderStageFlagBits)8192u
962 #endif // CTS_USES_VULKANSC
963 	};
964 
965 	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
966 }
967 
968 // Baseline version, to be used for shaders which don't specify a version
getBaselineSpirvVersion(const deUint32)969 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
970 {
971 	return vk::SPIRV_VERSION_1_0;
972 }
973 
974 // Max supported versions for each Vulkan version, without requiring a Vulkan extension.
getMaxSpirvVersionForVulkan(const deUint32 vulkanVersion)975 vk::SpirvVersion getMaxSpirvVersionForVulkan (const deUint32 vulkanVersion)
976 {
977 	vk::SpirvVersion	result			= vk::SPIRV_VERSION_LAST;
978 
979 	deUint32 vulkanVersionVariantMajorMinor = VK_MAKE_API_VERSION(VK_API_VERSION_VARIANT(vulkanVersion), VK_API_VERSION_MAJOR(vulkanVersion), VK_API_VERSION_MINOR(vulkanVersion), 0);
980 	if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_0)
981 		result = vk::SPIRV_VERSION_1_0;
982 	else if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_1)
983 		result = vk::SPIRV_VERSION_1_3;
984 #ifndef CTS_USES_VULKANSC
985 	else if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_2)
986 		result = vk::SPIRV_VERSION_1_5;
987 	else if (vulkanVersionVariantMajorMinor >= VK_API_VERSION_1_3)
988 		result = vk::SPIRV_VERSION_1_6;
989 #else
990 	else if (vulkanVersionVariantMajorMinor >= VK_API_VERSION_1_2)
991 		result = vk::SPIRV_VERSION_1_5;
992 #endif // CTS_USES_VULKANSC
993 
994 	DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
995 
996 	return result;
997 }
998 
getMaxSpirvVersionForAsm(const deUint32 vulkanVersion)999 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
1000 {
1001 	return getMaxSpirvVersionForVulkan(vulkanVersion);
1002 }
1003 
getMaxSpirvVersionForGlsl(const deUint32 vulkanVersion)1004 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
1005 {
1006 	return getMaxSpirvVersionForVulkan(vulkanVersion);
1007 }
1008 
extractSpirvVersion(const ProgramBinary & binary)1009 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
1010 {
1011 	DE_STATIC_ASSERT(SPIRV_VERSION_1_6 + 1 == SPIRV_VERSION_LAST);
1012 
1013 	if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
1014 		TCU_THROW(InternalError, "Binary is not in SPIR-V format");
1015 
1016 	if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
1017 		TCU_THROW(InternalError, "Invalid SPIR-V header format");
1018 
1019 	const deUint32				spirvBinaryVersion10	= 0x00010000;
1020 	const deUint32				spirvBinaryVersion11	= 0x00010100;
1021 	const deUint32				spirvBinaryVersion12	= 0x00010200;
1022 	const deUint32				spirvBinaryVersion13	= 0x00010300;
1023 	const deUint32				spirvBinaryVersion14	= 0x00010400;
1024 	const deUint32				spirvBinaryVersion15	= 0x00010500;
1025 	const deUint32				spirvBinaryVersion16	= 0x00010600;
1026 	const SpirvBinaryHeader*	header					= reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
1027 	const deUint32				spirvVersion			= isNativeSpirVBinaryEndianness()
1028 														? header->version
1029 														: deReverseBytes32(header->version);
1030 	SpirvVersion				result					= SPIRV_VERSION_LAST;
1031 
1032 	switch (spirvVersion)
1033 	{
1034 		case spirvBinaryVersion10:	result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
1035 		case spirvBinaryVersion11:	result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
1036 		case spirvBinaryVersion12:	result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
1037 		case spirvBinaryVersion13:	result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
1038 		case spirvBinaryVersion14:	result = SPIRV_VERSION_1_4; break; //!< SPIR-V 1.4
1039 		case spirvBinaryVersion15:	result = SPIRV_VERSION_1_5; break; //!< SPIR-V 1.5
1040 		case spirvBinaryVersion16:	result = SPIRV_VERSION_1_6; break; //!< SPIR-V 1.6
1041 		default:					TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
1042 	}
1043 
1044 	return result;
1045 }
1046 
getSpirvVersionName(const SpirvVersion spirvVersion)1047 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
1048 {
1049 	DE_STATIC_ASSERT(SPIRV_VERSION_1_6 + 1 == SPIRV_VERSION_LAST);
1050 	DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
1051 
1052 	std::string result;
1053 
1054 	switch (spirvVersion)
1055 	{
1056 		case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
1057 		case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
1058 		case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
1059 		case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
1060 		case SPIRV_VERSION_1_4: result = "1.4"; break; //!< SPIR-V 1.4
1061 		case SPIRV_VERSION_1_5: result = "1.5"; break; //!< SPIR-V 1.5
1062 		case SPIRV_VERSION_1_6: result = "1.6"; break; //!< SPIR-V 1.6
1063 		default:				result = "Unknown";
1064 	}
1065 
1066 	return result;
1067 }
1068 
operator ++(SpirvVersion & spirvVersion)1069 SpirvVersion& operator++(SpirvVersion& spirvVersion)
1070 {
1071 	if (spirvVersion == SPIRV_VERSION_LAST)
1072 		spirvVersion = SPIRV_VERSION_1_0;
1073 	else
1074 		spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
1075 
1076 	return spirvVersion;
1077 }
1078 
1079 } // vk
1080